CodeGenerator-arm64.cpp (139120B)
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/arm64/CodeGenerator-arm64.h" 8 9 #include "mozilla/DebugOnly.h" 10 #include "mozilla/MathAlgorithms.h" 11 12 #include "jsnum.h" 13 14 #include "jit/CodeGenerator.h" 15 #include "jit/InlineScriptTree.h" 16 #include "jit/JitRuntime.h" 17 #include "jit/MIR-wasm.h" 18 #include "jit/MIR.h" 19 #include "jit/MIRGraph.h" 20 #include "jit/ReciprocalMulConstants.h" 21 #include "vm/JSContext.h" 22 #include "vm/Realm.h" 23 #include "vm/Shape.h" 24 25 #include "jit/shared/CodeGenerator-shared-inl.h" 26 #include "vm/JSScript-inl.h" 27 28 using namespace js; 29 using namespace js::jit; 30 31 using JS::GenericNaN; 32 using mozilla::FloorLog2; 33 using mozilla::Maybe; 34 using mozilla::NegativeInfinity; 35 using mozilla::Nothing; 36 using mozilla::Some; 37 38 // shared 39 CodeGeneratorARM64::CodeGeneratorARM64(MIRGenerator* gen, LIRGraph* graph, 40 MacroAssembler* masm, 41 const wasm::CodeMetadata* wasmCodeMeta) 42 : CodeGeneratorShared(gen, graph, masm, wasmCodeMeta) {} 43 44 bool CodeGeneratorARM64::generateOutOfLineCode() { 45 AutoCreatedBy acb(masm, "CodeGeneratorARM64::generateOutOfLineCode"); 46 47 if (!CodeGeneratorShared::generateOutOfLineCode()) { 48 return false; 49 } 50 51 if (deoptLabel_.used()) { 52 // All non-table-based bailouts will go here. 53 masm.bind(&deoptLabel_); 54 55 // Store the frame size, so the handler can recover the IonScript. 56 masm.push(Imm32(frameSize())); 57 58 TrampolinePtr handler = gen->jitRuntime()->getGenericBailoutHandler(); 59 masm.jump(handler); 60 } 61 62 return !masm.oom(); 63 } 64 65 void CodeGeneratorARM64::emitBranch(Assembler::Condition cond, 66 MBasicBlock* mirTrue, 67 MBasicBlock* mirFalse) { 68 if (isNextBlock(mirFalse->lir())) { 69 jumpToBlock(mirTrue, cond); 70 } else { 71 jumpToBlock(mirFalse, Assembler::InvertCondition(cond)); 72 jumpToBlock(mirTrue); 73 } 74 } 75 76 void CodeGeneratorARM64::emitBailoutOOL(LSnapshot* snapshot) { 77 masm.push(Imm32(snapshot->snapshotOffset())); 78 masm.B(&deoptLabel_); 79 } 80 81 void CodeGeneratorARM64::bailoutIf(Assembler::Condition condition, 82 LSnapshot* snapshot) { 83 encode(snapshot); 84 85 InlineScriptTree* tree = snapshot->mir()->block()->trackedTree(); 86 auto* ool = new (alloc()) LambdaOutOfLineCode( 87 [=, this](OutOfLineCode& ool) { emitBailoutOOL(snapshot); }); 88 addOutOfLineCode(ool, 89 new (alloc()) BytecodeSite(tree, tree->script()->code())); 90 91 masm.B(ool->entry(), condition); 92 } 93 94 void CodeGeneratorARM64::bailoutIfZero(Assembler::Condition condition, 95 ARMRegister rt, LSnapshot* snapshot) { 96 MOZ_ASSERT(condition == Assembler::Zero || condition == Assembler::NonZero); 97 98 encode(snapshot); 99 100 InlineScriptTree* tree = snapshot->mir()->block()->trackedTree(); 101 auto* ool = new (alloc()) LambdaOutOfLineCode( 102 [=, this](OutOfLineCode& ool) { emitBailoutOOL(snapshot); }); 103 addOutOfLineCode(ool, 104 new (alloc()) BytecodeSite(tree, tree->script()->code())); 105 106 if (condition == Assembler::Zero) { 107 masm.Cbz(rt, ool->entry()); 108 } else { 109 masm.Cbnz(rt, ool->entry()); 110 } 111 } 112 113 void CodeGeneratorARM64::bailoutFrom(Label* label, LSnapshot* snapshot) { 114 MOZ_ASSERT_IF(!masm.oom(), label->used()); 115 MOZ_ASSERT_IF(!masm.oom(), !label->bound()); 116 117 encode(snapshot); 118 119 InlineScriptTree* tree = snapshot->mir()->block()->trackedTree(); 120 auto* ool = new (alloc()) LambdaOutOfLineCode( 121 [=, this](OutOfLineCode& ool) { emitBailoutOOL(snapshot); }); 122 addOutOfLineCode(ool, 123 new (alloc()) BytecodeSite(tree, tree->script()->code())); 124 125 masm.retarget(label, ool->entry()); 126 } 127 128 void CodeGeneratorARM64::bailout(LSnapshot* snapshot) { 129 Label label; 130 masm.b(&label); 131 bailoutFrom(&label, snapshot); 132 } 133 134 void CodeGenerator::visitMinMaxD(LMinMaxD* ins) { 135 ARMFPRegister lhs(ToFloatRegister(ins->first()), 64); 136 ARMFPRegister rhs(ToFloatRegister(ins->second()), 64); 137 ARMFPRegister output(ToFloatRegister(ins->output()), 64); 138 if (ins->mir()->isMax()) { 139 masm.Fmax(output, lhs, rhs); 140 } else { 141 masm.Fmin(output, lhs, rhs); 142 } 143 } 144 145 void CodeGenerator::visitMinMaxF(LMinMaxF* ins) { 146 ARMFPRegister lhs(ToFloatRegister(ins->first()), 32); 147 ARMFPRegister rhs(ToFloatRegister(ins->second()), 32); 148 ARMFPRegister output(ToFloatRegister(ins->output()), 32); 149 if (ins->mir()->isMax()) { 150 masm.Fmax(output, lhs, rhs); 151 } else { 152 masm.Fmin(output, lhs, rhs); 153 } 154 } 155 156 template <typename T> 157 static ARMRegister toWRegister(const T* a) { 158 return ARMRegister(ToRegister(a), 32); 159 } 160 161 template <typename T> 162 static ARMRegister toXRegister(const T* a) { 163 return ARMRegister(ToRegister(a), 64); 164 } 165 166 static ARMRegister toXRegister(const LInt64Allocation& a) { 167 return ARMRegister(ToRegister64(a).reg, 64); 168 } 169 170 static Operand toWOperand(const LAllocation* a) { 171 if (a->isConstant()) { 172 return Operand(ToInt32(a)); 173 } 174 return Operand(toWRegister(a)); 175 } 176 177 static Operand toXOperand(const LAllocation* a) { 178 if (a->isConstant()) { 179 return Operand(ToIntPtr(a)); 180 } 181 return Operand(toXRegister(a)); 182 } 183 184 static Operand toXOperand(const LInt64Allocation& a) { 185 if (IsConstant(a)) { 186 return Operand(ToInt64(a)); 187 } 188 return Operand(toXRegister(a)); 189 } 190 191 void CodeGenerator::visitAddI(LAddI* ins) { 192 const LAllocation* lhs = ins->lhs(); 193 const LAllocation* rhs = ins->rhs(); 194 const LDefinition* dest = ins->output(); 195 196 // Platforms with three-operand arithmetic ops don't need recovery. 197 MOZ_ASSERT(!ins->recoversInput()); 198 199 if (ins->snapshot()) { 200 masm.Adds(toWRegister(dest), toWRegister(lhs), toWOperand(rhs)); 201 bailoutIf(Assembler::Overflow, ins->snapshot()); 202 } else { 203 masm.Add(toWRegister(dest), toWRegister(lhs), toWOperand(rhs)); 204 } 205 } 206 207 void CodeGenerator::visitSubI(LSubI* ins) { 208 const LAllocation* lhs = ins->lhs(); 209 const LAllocation* rhs = ins->rhs(); 210 const LDefinition* dest = ins->output(); 211 212 // Platforms with three-operand arithmetic ops don't need recovery. 213 MOZ_ASSERT(!ins->recoversInput()); 214 215 if (ins->snapshot()) { 216 masm.Subs(toWRegister(dest), toWRegister(lhs), toWOperand(rhs)); 217 bailoutIf(Assembler::Overflow, ins->snapshot()); 218 } else { 219 masm.Sub(toWRegister(dest), toWRegister(lhs), toWOperand(rhs)); 220 } 221 } 222 223 void CodeGenerator::visitMulI(LMulI* ins) { 224 const LAllocation* lhs = ins->lhs(); 225 const LAllocation* rhs = ins->rhs(); 226 const LDefinition* dest = ins->output(); 227 MMul* mul = ins->mir(); 228 MOZ_ASSERT_IF(mul->mode() == MMul::Integer, 229 !mul->canBeNegativeZero() && !mul->canOverflow()); 230 231 Register lhsreg = ToRegister(lhs); 232 const ARMRegister lhsreg32 = ARMRegister(lhsreg, 32); 233 Register destreg = ToRegister(dest); 234 const ARMRegister destreg32 = ARMRegister(destreg, 32); 235 236 if (rhs->isConstant()) { 237 // Bailout on -0.0. 238 int32_t constant = ToInt32(rhs); 239 if (mul->canBeNegativeZero() && constant <= 0) { 240 Assembler::Condition bailoutCond = 241 (constant == 0) ? Assembler::LessThan : Assembler::Equal; 242 bailoutCmp32(bailoutCond, lhsreg, Imm32(0), ins->snapshot()); 243 } 244 245 switch (constant) { 246 case -1: 247 masm.Negs(destreg32, Operand(lhsreg32)); 248 break; // Go to overflow check. 249 case 0: 250 masm.Mov(destreg32, wzr); 251 return; // Avoid overflow check. 252 case 1: 253 if (destreg != lhsreg) { 254 masm.Mov(destreg32, lhsreg32); 255 } 256 return; // Avoid overflow check. 257 case 2: 258 if (!mul->canOverflow()) { 259 masm.Add(destreg32, lhsreg32, Operand(lhsreg32)); 260 return; // Avoid overflow check. 261 } 262 masm.Adds(destreg32, lhsreg32, Operand(lhsreg32)); 263 break; // Go to overflow check. 264 default: 265 // Use shift if cannot overflow and constant is a power of 2 266 if (!mul->canOverflow() && constant > 0) { 267 int32_t shift = FloorLog2(constant); 268 if ((1 << shift) == constant) { 269 masm.Lsl(destreg32, lhsreg32, shift); 270 return; 271 } 272 } 273 274 // Otherwise, just multiply. We have to check for overflow. 275 // Negative zero was handled above. 276 Label bailout; 277 Label* onOverflow = mul->canOverflow() ? &bailout : nullptr; 278 279 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 280 const Register scratch = temps.AcquireW().asUnsized(); 281 282 masm.move32(Imm32(constant), scratch); 283 masm.mul32(lhsreg, scratch, destreg, onOverflow); 284 285 if (onOverflow) { 286 bailoutFrom(&bailout, ins->snapshot()); 287 } 288 return; 289 } 290 291 // Overflow check. 292 if (mul->canOverflow()) { 293 bailoutIf(Assembler::Overflow, ins->snapshot()); 294 } 295 } else { 296 Register rhsreg = ToRegister(rhs); 297 const ARMRegister rhsreg32 = ARMRegister(rhsreg, 32); 298 299 Label bailout; 300 Label* onOverflow = mul->canOverflow() ? &bailout : nullptr; 301 302 if (mul->canBeNegativeZero()) { 303 // The product of two integer operands is negative zero iff one 304 // operand is zero, and the other is negative. Therefore, the 305 // sum of the two operands will also be negative (specifically, 306 // it will be the non-zero operand). If the result of the 307 // multiplication is 0, we can check the sign of the sum to 308 // determine whether we should bail out. 309 310 // This code can bailout, so lowering guarantees that the input 311 // operands are not overwritten. 312 MOZ_ASSERT(destreg != lhsreg); 313 MOZ_ASSERT(destreg != rhsreg); 314 315 // Do the multiplication. 316 masm.mul32(lhsreg, rhsreg, destreg, onOverflow); 317 318 // Set Zero flag if destreg is 0. 319 masm.test32(destreg, destreg); 320 321 // ccmn is 'conditional compare negative'. 322 // If the Zero flag is set: 323 // perform a compare negative (compute lhs+rhs and set flags) 324 // else: 325 // clear flags 326 masm.Ccmn(lhsreg32, rhsreg32, vixl::NoFlag, Assembler::Zero); 327 328 // Bails out if (lhs * rhs == 0) && (lhs + rhs < 0): 329 bailoutIf(Assembler::LessThan, ins->snapshot()); 330 331 } else { 332 masm.mul32(lhsreg, rhsreg, destreg, onOverflow); 333 } 334 if (onOverflow) { 335 bailoutFrom(&bailout, ins->snapshot()); 336 } 337 } 338 } 339 340 template <class LIR> 341 static void TrapIfDivideByZero(MacroAssembler& masm, LIR* lir, 342 ARMRegister rhs) { 343 auto* mir = lir->mir(); 344 MOZ_ASSERT(mir->trapOnError()); 345 346 if (mir->canBeDivideByZero()) { 347 Label nonZero; 348 masm.Cbnz(rhs, &nonZero); 349 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc()); 350 masm.bind(&nonZero); 351 } 352 } 353 354 void CodeGenerator::visitDivI(LDivI* ins) { 355 Register lhs = ToRegister(ins->lhs()); 356 Register rhs = ToRegister(ins->rhs()); 357 358 ARMRegister lhs32 = toWRegister(ins->lhs()); 359 ARMRegister rhs32 = toWRegister(ins->rhs()); 360 ARMRegister output32 = toWRegister(ins->output()); 361 362 MDiv* mir = ins->mir(); 363 364 // Handle division by zero. 365 if (mir->canBeDivideByZero()) { 366 if (mir->trapOnError()) { 367 TrapIfDivideByZero(masm, ins, rhs32); 368 } else if (mir->canTruncateInfinities()) { 369 // SDIV returns zero for division by zero, exactly what we want for 370 // truncated division. Remainder computation expects a non-zero divisor, 371 // so we must also be allowed to truncate the remainder. 372 MOZ_ASSERT(mir->canTruncateRemainder(), 373 "remainder computation expects a non-zero divisor"); 374 } else { 375 MOZ_ASSERT(mir->fallible()); 376 bailoutTest32(Assembler::Zero, rhs, rhs, ins->snapshot()); 377 } 378 } 379 380 // Handle an integer overflow from (INT32_MIN / -1). 381 // The integer division gives INT32_MIN, but should be -(double)INT32_MIN. 382 // 383 // SDIV returns INT32_MIN for (INT32_MIN / -1), so no extra code needed when 384 // truncation is allowed. 385 if (mir->canBeNegativeOverflow() && 386 (mir->trapOnError() || !mir->canTruncateOverflow())) { 387 Label notOverflow; 388 389 // Branch to handle the non-overflow cases. 390 masm.branch32(Assembler::NotEqual, lhs, Imm32(INT32_MIN), ¬Overflow); 391 392 // Handle overflow. 393 if (mir->trapOnError()) { 394 masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), ¬Overflow); 395 masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->trapSiteDesc()); 396 } else { 397 MOZ_ASSERT(mir->fallible()); 398 bailoutCmp32(Assembler::Equal, rhs, Imm32(-1), ins->snapshot()); 399 } 400 masm.bind(¬Overflow); 401 } 402 403 // Handle negative zero: lhs == 0 && rhs < 0. 404 if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) { 405 Label nonZero; 406 masm.branch32(Assembler::NotEqual, lhs, Imm32(0), &nonZero); 407 bailoutCmp32(Assembler::LessThan, rhs, Imm32(0), ins->snapshot()); 408 masm.bind(&nonZero); 409 } 410 411 // Perform integer division. 412 masm.Sdiv(output32, lhs32, rhs32); 413 414 if (!mir->canTruncateRemainder()) { 415 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 416 ARMRegister remainder32 = temps.AcquireW(); 417 Register remainder = remainder32.asUnsized(); 418 419 // Compute the remainder: remainder = lhs - (output * rhs). 420 masm.Msub(remainder32, output32, rhs32, lhs32); 421 422 bailoutTest32(Assembler::NonZero, remainder, remainder, ins->snapshot()); 423 } 424 } 425 426 void CodeGenerator::visitDivPowTwoI(LDivPowTwoI* ins) { 427 const Register numerator = ToRegister(ins->numerator()); 428 const ARMRegister numerator32 = toWRegister(ins->numerator()); 429 const ARMRegister output32 = toWRegister(ins->output()); 430 431 int32_t shift = ins->shift(); 432 bool negativeDivisor = ins->negativeDivisor(); 433 MDiv* mir = ins->mir(); 434 435 if (!mir->isTruncated() && negativeDivisor) { 436 // 0 divided by a negative number returns a -0 double. 437 bailoutTest32(Assembler::Zero, numerator, numerator, ins->snapshot()); 438 } 439 440 if (shift) { 441 if (!mir->isTruncated()) { 442 // If the remainder is != 0, bailout since this must be a double. 443 bailoutTest32(Assembler::NonZero, numerator, 444 Imm32(UINT32_MAX >> (32 - shift)), ins->snapshot()); 445 } 446 447 if (mir->isUnsigned()) { 448 // shift right 449 masm.Lsr(output32, numerator32, shift); 450 } else { 451 ARMRegister temp32 = numerator32; 452 // Adjust the value so that shifting produces a correctly 453 // rounded result when the numerator is negative. See 10-1 454 // "Signed Division by a Known Power of 2" in Henry 455 // S. Warren, Jr.'s Hacker's Delight. 456 if (mir->canBeNegativeDividend() && mir->isTruncated()) { 457 if (shift > 1) { 458 // Copy the sign bit of the numerator. (= (2^32 - 1) or 0) 459 masm.Asr(output32, numerator32, 31); 460 temp32 = output32; 461 } 462 // Divide by 2^(32 - shift) 463 // i.e. (= (2^32 - 1) / 2^(32 - shift) or 0) 464 // i.e. (= (2^shift - 1) or 0) 465 masm.Lsr(output32, temp32, 32 - shift); 466 // If signed, make any 1 bit below the shifted bits to bubble up, such 467 // that once shifted the value would be rounded towards 0. 468 masm.Add(output32, output32, numerator32); 469 temp32 = output32; 470 } 471 masm.Asr(output32, temp32, shift); 472 473 if (negativeDivisor) { 474 masm.Neg(output32, output32); 475 } 476 } 477 return; 478 } 479 480 if (negativeDivisor) { 481 // INT32_MIN / -1 overflows. 482 if (!mir->isTruncated()) { 483 masm.Negs(output32, numerator32); 484 bailoutIf(Assembler::Overflow, ins->snapshot()); 485 } else if (mir->trapOnError()) { 486 Label ok; 487 masm.Negs(output32, numerator32); 488 masm.branch(Assembler::NoOverflow, &ok); 489 masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->trapSiteDesc()); 490 masm.bind(&ok); 491 } else { 492 // Do not set condition flags. 493 masm.Neg(output32, numerator32); 494 } 495 } else { 496 if (mir->isUnsigned() && !mir->isTruncated()) { 497 // Copy and set flags. 498 masm.Adds(output32, numerator32, 0); 499 // Unsigned division by 1 can overflow if output is not truncated, as we 500 // do not have an Unsigned type for MIR instructions. 501 bailoutIf(Assembler::Signed, ins->snapshot()); 502 } else { 503 // Copy the result. 504 masm.Mov(output32, numerator32); 505 } 506 } 507 } 508 509 void CodeGenerator::visitDivPowTwoI64(LDivPowTwoI64* ins) { 510 ARMRegister numerator64 = toXRegister(ins->numerator()); 511 ARMRegister output64 = toXRegister(ins->output()); 512 513 int32_t shift = ins->shift(); 514 bool negativeDivisor = ins->negativeDivisor(); 515 MDiv* mir = ins->mir(); 516 517 if (shift) { 518 if (mir->isUnsigned()) { 519 // shift right 520 masm.Lsr(output64, numerator64, shift); 521 } else { 522 ARMRegister temp64 = numerator64; 523 // Adjust the value so that shifting produces a correctly 524 // rounded result when the numerator is negative. See 10-1 525 // "Signed Division by a Known Power of 2" in Henry 526 // S. Warren, Jr.'s Hacker's Delight. 527 if (mir->canBeNegativeDividend()) { 528 if (shift > 1) { 529 // Copy the sign bit of the numerator. (= (2^64 - 1) or 0) 530 masm.Asr(output64, numerator64, 63); 531 temp64 = output64; 532 } 533 // Divide by 2^(64 - shift) 534 // i.e. (= (2^64 - 1) / 2^(64 - shift) or 0) 535 // i.e. (= (2^shift - 1) or 0) 536 masm.Lsr(output64, temp64, 64 - shift); 537 // If signed, make any 1 bit below the shifted bits to bubble up, such 538 // that once shifted the value would be rounded towards 0. 539 masm.Add(output64, output64, numerator64); 540 temp64 = output64; 541 } 542 masm.Asr(output64, temp64, shift); 543 544 if (negativeDivisor) { 545 masm.Neg(output64, output64); 546 } 547 } 548 return; 549 } 550 551 if (negativeDivisor) { 552 // INT64_MIN / -1 overflows. 553 Label ok; 554 masm.Negs(output64, numerator64); 555 masm.branch(Assembler::NoOverflow, &ok); 556 masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->trapSiteDesc()); 557 masm.bind(&ok); 558 } else { 559 // Copy the result. 560 masm.Mov(output64, numerator64); 561 } 562 } 563 564 template <class LDivOrMod> 565 static void DivideWithConstant(MacroAssembler& masm, LDivOrMod* ins) { 566 ARMRegister lhs32 = toWRegister(ins->numerator()); 567 ARMRegister lhs64 = toXRegister(ins->numerator()); 568 ARMRegister output32 = toWRegister(ins->output()); 569 ARMRegister output64 = toXRegister(ins->output()); 570 int32_t d = ins->denominator(); 571 572 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 573 ARMRegister const32 = temps.AcquireW(); 574 575 // The absolute value of the denominator isn't a power of 2. 576 MOZ_ASSERT(!mozilla::IsPowerOfTwo(mozilla::Abs(d))); 577 578 auto* mir = ins->mir(); 579 580 // We will first divide by Abs(d), and negate the answer if d is negative. 581 // If desired, this can be avoided by generalizing computeDivisionConstants. 582 auto rmc = ReciprocalMulConstants::computeSignedDivisionConstants(d); 583 584 // We first compute (M * n) >> 32, where M = rmc.multiplier. 585 masm.Mov(const32, int32_t(rmc.multiplier)); 586 if (rmc.multiplier > INT32_MAX) { 587 MOZ_ASSERT(rmc.multiplier < (int64_t(1) << 32)); 588 589 // We actually compute (int32_t(M) * n) instead, without the upper bit. 590 // Thus, (M * n) = (int32_t(M) * n) + n << 32. 591 // 592 // ((int32_t(M) * n) + n << 32) can't overflow, as both operands have 593 // opposite signs because int32_t(M) is negative. 594 masm.Lsl(output64, lhs64, 32); 595 596 // Store (M * n) in output64. 597 masm.Smaddl(output64, const32, lhs32, output64); 598 } else { 599 // Store (M * n) in output64. 600 masm.Smull(output64, const32, lhs32); 601 } 602 603 // (M * n) >> (32 + shift) is the truncated division answer if n is 604 // non-negative, as proved in the comments of computeDivisionConstants. We 605 // must add 1 later if n is negative to get the right answer in all cases. 606 masm.Asr(output64, output64, 32 + rmc.shiftAmount); 607 608 // We'll subtract -1 instead of adding 1, because (n < 0 ? -1 : 0) can be 609 // computed with just a sign-extending shift of 31 bits. 610 if (mir->canBeNegativeDividend()) { 611 masm.Sub(output32, output32, Operand(lhs32, vixl::ASR, 31)); 612 } 613 614 // After this, output32 contains the correct truncated division result. 615 if (d < 0) { 616 masm.Neg(output32, output32); 617 } 618 } 619 620 void CodeGenerator::visitDivConstantI(LDivConstantI* ins) { 621 ARMRegister lhs32 = toWRegister(ins->numerator()); 622 ARMRegister output32 = toWRegister(ins->output()); 623 int32_t d = ins->denominator(); 624 625 MDiv* mir = ins->mir(); 626 627 if (d == 0) { 628 if (mir->trapOnError()) { 629 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc()); 630 } else if (mir->canTruncateInfinities()) { 631 masm.Mov(output32, wzr); 632 } else { 633 MOZ_ASSERT(mir->fallible()); 634 bailout(ins->snapshot()); 635 } 636 return; 637 } 638 639 // Compute the truncated division result in output32. 640 DivideWithConstant(masm, ins); 641 642 if (!mir->isTruncated()) { 643 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 644 ARMRegister temp32 = temps.AcquireW(); 645 Register temp = temp32.asUnsized(); 646 647 // This is a division op. Multiply the obtained value by d to check if 648 // the correct answer is an integer. This cannot overflow, since |d| > 1. 649 masm.Mov(temp32, d); 650 masm.Msub(temp32, output32, temp32, lhs32); 651 652 if (d > 0) { 653 // bailout if (lhs - output * d != 0) 654 bailoutTest32(Assembler::NonZero, temp, temp, ins->snapshot()); 655 } else { 656 MOZ_ASSERT(d < 0); 657 658 // bailout if (lhs - output * d != 0) 659 masm.Cmp(temp32, wzr); 660 661 // If lhs is zero and the divisor is negative, the answer should have 662 // been -0. 663 // 664 // or bailout if (lhs == 0). 665 // ^ ^ 666 // | '-- masm.Ccmp(lhs32, lhs32, .., ..) 667 // '-- masm.Ccmp(.., .., vixl::ZFlag, Assembler::Zero) 668 masm.Ccmp(lhs32, wzr, vixl::ZFlag, Assembler::Zero); 669 670 // bailout if (lhs - output * d != 0) or (lhs == 0) 671 bailoutIf(Assembler::Zero, ins->snapshot()); 672 } 673 } 674 } 675 676 template <class LUDivOrUMod> 677 static void UnsignedDivideWithConstant(MacroAssembler& masm, LUDivOrUMod* ins) { 678 ARMRegister lhs32 = toWRegister(ins->numerator()); 679 ARMRegister lhs64 = toXRegister(ins->numerator()); 680 ARMRegister output64 = toXRegister(ins->output()); 681 uint32_t d = ins->denominator(); 682 683 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 684 ARMRegister const32 = temps.AcquireW(); 685 686 // The denominator isn't a power of 2 (see LDivPowTwoI). 687 MOZ_ASSERT(!mozilla::IsPowerOfTwo(d)); 688 689 auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(d); 690 691 // We first compute (M * n), where M = rmc.multiplier. 692 masm.Mov(const32, int32_t(rmc.multiplier)); 693 masm.Umull(output64, const32, lhs32); 694 if (rmc.multiplier > UINT32_MAX) { 695 // M >= 2^32 and shift == 0 is impossible, as d >= 2 implies that 696 // ((M * n) >> (32 + shift)) >= n > floor(n/d) whenever n >= d, 697 // contradicting the proof of correctness in computeDivisionConstants. 698 MOZ_ASSERT(rmc.shiftAmount > 0); 699 MOZ_ASSERT(rmc.multiplier < (int64_t(1) << 33)); 700 701 // We actually compute (uint32_t(M) * n) instead, without the upper bit. 702 // Thus, (M * n) = (uint32_t(M) * n) + n << 32. 703 // 704 // ((uint32_t(M) * n) + n << 32) can overflow. Hacker's Delight explains a 705 // trick to avoid this overflow case, but we can avoid it by computing the 706 // addition on 64 bits registers. 707 // 708 // Compute ((uint32_t(M) * n) >> 32 + n) 709 masm.Add(output64, lhs64, Operand(output64, vixl::LSR, 32)); 710 711 // (M * n) >> (32 + shift) is the truncated division answer. 712 masm.Lsr(output64, output64, rmc.shiftAmount); 713 } else { 714 // (M * n) >> (32 + shift) is the truncated division answer. 715 masm.Lsr(output64, output64, 32 + rmc.shiftAmount); 716 } 717 } 718 719 void CodeGenerator::visitUDivConstant(LUDivConstant* ins) { 720 ARMRegister lhs32 = toWRegister(ins->numerator()); 721 ARMRegister output32 = toWRegister(ins->output()); 722 uint32_t d = ins->denominator(); 723 724 MDiv* mir = ins->mir(); 725 726 if (d == 0) { 727 if (ins->mir()->trapOnError()) { 728 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc()); 729 } else if (mir->canTruncateInfinities()) { 730 masm.Mov(output32, wzr); 731 } else { 732 MOZ_ASSERT(mir->fallible()); 733 bailout(ins->snapshot()); 734 } 735 return; 736 } 737 738 // Compute the truncated division result in output32. 739 UnsignedDivideWithConstant(masm, ins); 740 741 // We now have the truncated division value. We are checking whether the 742 // division resulted in an integer, we multiply the obtained value by d and 743 // check the remainder of the division. 744 if (!mir->isTruncated()) { 745 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 746 ARMRegister temp32 = temps.AcquireW(); 747 Register temp = temp32.asUnsized(); 748 749 masm.Mov(temp32, d); 750 masm.Msub(temp32, output32, temp32, lhs32); 751 752 // bailout if (lhs - output * d != 0) 753 bailoutTest32(Assembler::NonZero, temp, temp, ins->snapshot()); 754 } 755 } 756 757 template <class LDivOrMod> 758 static void Divide64WithConstant(MacroAssembler& masm, LDivOrMod* ins) { 759 ARMRegister lhs64 = toXRegister(ins->numerator()); 760 ARMRegister output64 = toXRegister(ins->output()); 761 int64_t d = ins->denominator(); 762 763 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 764 ARMRegister const64 = temps.AcquireX(); 765 766 // The absolute value of the denominator isn't a power of 2. 767 MOZ_ASSERT(!mozilla::IsPowerOfTwo(mozilla::Abs(d))); 768 769 auto* mir = ins->mir(); 770 771 // We will first divide by Abs(d), and negate the answer if d is negative. 772 // If desired, this can be avoided by generalizing computeDivisionConstants. 773 auto rmc = ReciprocalMulConstants::computeSignedDivisionConstants(d); 774 775 // We first compute (M * n) >> 64, where M = rmc.multiplier. 776 masm.Mov(const64, uint64_t(rmc.multiplier)); 777 masm.Smulh(output64, lhs64, const64); 778 if (rmc.multiplier > Int128(INT64_MAX)) { 779 MOZ_ASSERT(rmc.multiplier < (Int128(1) << 64)); 780 781 // We actually computed output = ((int64_t(M) * n) >> 64) instead. Since 782 // (M * n) >> 64 is the same as (output + n), we can correct for the 783 // overflow. (output + n) can't overflow, as n and output have opposite 784 // signs because int64_t(M) is negative. 785 masm.Add(output64, output64, lhs64); 786 } 787 788 // (M * n) >> (64 + shift) is the truncated division answer if n is 789 // non-negative, as proved in the comments of computeDivisionConstants. We 790 // must add 1 later if n is negative to get the right answer in all cases. 791 masm.Asr(output64, output64, rmc.shiftAmount); 792 793 // We'll subtract -1 instead of adding 1, because (n < 0 ? -1 : 0) can be 794 // computed with just a sign-extending shift of 63 bits. 795 if (mir->canBeNegativeDividend()) { 796 masm.Sub(output64, output64, Operand(lhs64, vixl::ASR, 63)); 797 } 798 799 // After this, output64 contains the correct truncated division result. 800 if (d < 0) { 801 masm.Neg(output64, output64); 802 } 803 } 804 805 void CodeGenerator::visitDivConstantI64(LDivConstantI64* ins) { 806 int64_t d = ins->denominator(); 807 808 if (d == 0) { 809 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc()); 810 return; 811 } 812 813 // Compute the truncated division result. 814 Divide64WithConstant(masm, ins); 815 } 816 817 template <class LUDivOrUMod> 818 static void UnsignedDivide64WithConstant(MacroAssembler& masm, 819 LUDivOrUMod* ins) { 820 ARMRegister lhs64 = toXRegister(ins->numerator()); 821 ARMRegister output64 = toXRegister(ins->output()); 822 uint64_t d = ins->denominator(); 823 824 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 825 ARMRegister const64 = temps.AcquireX(); 826 827 // The denominator isn't a power of 2 (see LDivPowTwoI). 828 MOZ_ASSERT(!mozilla::IsPowerOfTwo(d)); 829 830 auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(d); 831 832 // We first compute (M * n) >> 64, where M = rmc.multiplier. 833 masm.Mov(const64, uint64_t(rmc.multiplier)); 834 masm.Umulh(output64, lhs64, const64); 835 if (rmc.multiplier > Int128(UINT64_MAX)) { 836 // M >= 2^64 and shift == 0 is impossible, as d >= 2 implies that 837 // ((M * n) >> (64 + shift)) >= n > floor(n/d) whenever n >= d, 838 // contradicting the proof of correctness in computeDivisionConstants. 839 MOZ_ASSERT(rmc.shiftAmount > 0); 840 MOZ_ASSERT(rmc.multiplier < (Int128(1) << 65)); 841 842 // We actually computed output = ((uint64_t(M) * n) >> 64) instead. Since 843 // (M * n) >> (64 + shift) is the same as (output + n) >> shift, we can 844 // correct for the overflow. This case is a bit trickier than the signed 845 // case, though, as the (output + n) addition itself can overflow; however, 846 // note that 847 // (output + n) >> shift == (((n - output) >> 1) + output) >> (shift - 1), 848 // which is overflow-free. See Hacker's Delight, section 10-8 for details. 849 850 masm.Sub(const64, lhs64, output64); 851 masm.Add(output64, output64, Operand(const64, vixl::LSR, 1)); 852 masm.Lsr(output64, output64, rmc.shiftAmount - 1); 853 } else { 854 masm.Lsr(output64, output64, rmc.shiftAmount); 855 } 856 } 857 858 void CodeGenerator::visitUDivConstantI64(LUDivConstantI64* ins) { 859 uint64_t d = ins->denominator(); 860 861 if (d == 0) { 862 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc()); 863 return; 864 } 865 866 // Compute the truncated division result. 867 UnsignedDivide64WithConstant(masm, ins); 868 } 869 870 void CodeGenerator::visitModI(LModI* ins) { 871 Register lhs = ToRegister(ins->lhs()); 872 Register rhs = ToRegister(ins->rhs()); 873 874 ARMRegister lhs32 = toWRegister(ins->lhs()); 875 ARMRegister rhs32 = toWRegister(ins->rhs()); 876 ARMRegister output32 = toWRegister(ins->output()); 877 Label done; 878 879 MMod* mir = ins->mir(); 880 881 // Prevent divide by zero. 882 if (mir->canBeDivideByZero()) { 883 if (mir->trapOnError()) { 884 TrapIfDivideByZero(masm, ins, rhs32); 885 } else if (mir->isTruncated()) { 886 // Truncated division by zero yields integer zero. 887 masm.Mov(output32, wzr); 888 masm.Cbz(rhs32, &done); 889 } else { 890 // Non-truncated division by zero produces a non-integer. 891 MOZ_ASSERT(mir->fallible()); 892 bailoutTest32(Assembler::Zero, rhs, rhs, ins->snapshot()); 893 } 894 } 895 896 // Signed division. 897 masm.Sdiv(output32, lhs32, rhs32); 898 899 // Compute the remainder: output = lhs - (output * rhs). 900 masm.Msub(output32, output32, rhs32, lhs32); 901 902 if (mir->canBeNegativeDividend() && !mir->isTruncated()) { 903 // If output == 0 and lhs < 0, then the result should be double -0.0. 904 // Note that this guard handles lhs == INT_MIN and rhs == -1: 905 // output = INT_MIN - (INT_MIN / -1) * -1 906 // = INT_MIN - INT_MIN 907 // = 0 908 masm.Cbnz(output32, &done); 909 bailoutCmp32(Assembler::LessThan, lhs, Imm32(0), ins->snapshot()); 910 } 911 912 if (done.used()) { 913 masm.bind(&done); 914 } 915 } 916 917 void CodeGenerator::visitModPowTwoI(LModPowTwoI* ins) { 918 Register lhs = ToRegister(ins->input()); 919 ARMRegister lhsw = toWRegister(ins->input()); 920 ARMRegister outw = toWRegister(ins->output()); 921 922 int32_t shift = ins->shift(); 923 bool canBeNegative = 924 !ins->mir()->isUnsigned() && ins->mir()->canBeNegativeDividend(); 925 926 if (shift == 0) { 927 if (canBeNegative && !ins->mir()->isTruncated()) { 928 bailoutTest32(Assembler::Signed, lhs, lhs, ins->snapshot()); 929 } 930 masm.Mov(outw, wzr); 931 return; 932 } 933 934 Label negative; 935 if (canBeNegative) { 936 // Switch based on sign of the lhs. 937 // Positive numbers are just a bitmask. 938 masm.branchTest32(Assembler::Signed, lhs, lhs, &negative); 939 } 940 941 masm.And(outw, lhsw, Operand((uint32_t(1) << shift) - 1)); 942 943 if (canBeNegative) { 944 Label done; 945 masm.jump(&done); 946 947 // Negative numbers need a negate, bitmask, negate. 948 masm.bind(&negative); 949 masm.Neg(outw, Operand(lhsw)); 950 masm.And(outw, outw, Operand((uint32_t(1) << shift) - 1)); 951 952 // Since a%b has the same sign as b, and a is negative in this branch, 953 // an answer of 0 means the correct result is actually -0. Bail out. 954 if (!ins->mir()->isTruncated()) { 955 masm.Negs(outw, Operand(outw)); 956 bailoutIf(Assembler::Zero, ins->snapshot()); 957 } else { 958 masm.Neg(outw, Operand(outw)); 959 } 960 961 masm.bind(&done); 962 } 963 } 964 965 void CodeGenerator::visitModPowTwoI64(LModPowTwoI64* ins) { 966 Register lhs = ToRegister(ins->input()); 967 ARMRegister lhs64 = toXRegister(ins->input()); 968 ARMRegister out64 = toXRegister(ins->output()); 969 970 int32_t shift = ins->shift(); 971 bool canBeNegative = 972 !ins->mir()->isUnsigned() && ins->mir()->canBeNegativeDividend(); 973 974 if (shift == 0) { 975 masm.Mov(out64, xzr); 976 return; 977 } 978 979 auto clearHighBits = [&](ARMRegister reg) { 980 switch (shift) { 981 case 32: 982 masm.Mov(out64.W(), reg.W()); 983 break; 984 default: 985 masm.And(out64, reg, Operand((uint64_t(1) << shift) - 1)); 986 break; 987 } 988 }; 989 990 Label negative; 991 if (canBeNegative) { 992 // Switch based on sign of the lhs. 993 // Positive numbers are just a bitmask. 994 masm.branchTestPtr(Assembler::Signed, lhs, lhs, &negative); 995 } 996 997 clearHighBits(lhs64); 998 999 if (canBeNegative) { 1000 Label done; 1001 masm.jump(&done); 1002 1003 // Negative numbers need a negate, bitmask, negate. 1004 masm.bind(&negative); 1005 masm.Neg(out64, Operand(lhs64)); 1006 clearHighBits(out64); 1007 masm.Neg(out64, Operand(out64)); 1008 1009 masm.bind(&done); 1010 } 1011 } 1012 1013 void CodeGenerator::visitModConstantI(LModConstantI* ins) { 1014 Register lhs = ToRegister(ins->numerator()); 1015 ARMRegister lhs32 = toWRegister(ins->numerator()); 1016 ARMRegister output32 = toWRegister(ins->output()); 1017 1018 MMod* mir = ins->mir(); 1019 1020 int32_t d = ins->denominator(); 1021 if (d == 0) { 1022 if (mir->trapOnError()) { 1023 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc()); 1024 } else if (mir->isTruncated()) { 1025 masm.Mov(output32, wzr); 1026 } else { 1027 MOZ_ASSERT(mir->fallible()); 1028 bailout(ins->snapshot()); 1029 } 1030 return; 1031 } 1032 1033 // Compute the truncated division result in output32. 1034 DivideWithConstant(masm, ins); 1035 1036 // Compute the remainder: output = lhs - (output * rhs). 1037 { 1038 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 1039 ARMRegister rhs32 = temps.AcquireW(); 1040 1041 masm.Mov(rhs32, d); 1042 masm.Msub(output32, output32, rhs32, lhs32); 1043 } 1044 1045 if (mir->canBeNegativeDividend() && !mir->isTruncated()) { 1046 // If output == 0 and lhs < 0, then the result should be double -0.0. 1047 Label done; 1048 masm.Cbnz(output32, &done); 1049 bailoutCmp32(Assembler::LessThan, lhs, Imm32(0), ins->snapshot()); 1050 masm.bind(&done); 1051 } 1052 } 1053 1054 void CodeGenerator::visitUModConstant(LUModConstant* ins) { 1055 Register output = ToRegister(ins->output()); 1056 ARMRegister lhs32 = toWRegister(ins->numerator()); 1057 ARMRegister output32 = toWRegister(ins->output()); 1058 1059 MMod* mir = ins->mir(); 1060 1061 uint32_t d = ins->denominator(); 1062 if (d == 0) { 1063 if (ins->mir()->trapOnError()) { 1064 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc()); 1065 } else if (mir->isTruncated()) { 1066 masm.Mov(output32, wzr); 1067 } else { 1068 MOZ_ASSERT(mir->fallible()); 1069 bailout(ins->snapshot()); 1070 } 1071 return; 1072 } 1073 1074 // Compute the truncated division result in output32. 1075 UnsignedDivideWithConstant(masm, ins); 1076 1077 // Compute the remainder: output = lhs - (output * rhs). 1078 { 1079 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 1080 ARMRegister rhs32 = temps.AcquireW(); 1081 1082 masm.Mov(rhs32, d); 1083 masm.Msub(output32, output32, rhs32, lhs32); 1084 } 1085 1086 // Bail if not truncated and the remainder is in the range [2^31, 2^32). 1087 if (!ins->mir()->isTruncated()) { 1088 bailoutTest32(Assembler::Signed, output, output, ins->snapshot()); 1089 } 1090 } 1091 1092 void CodeGenerator::visitModConstantI64(LModConstantI64* ins) { 1093 ARMRegister lhs64 = toXRegister(ins->numerator()); 1094 ARMRegister output64 = toXRegister(ins->output()); 1095 1096 int64_t d = ins->denominator(); 1097 1098 if (d == 0) { 1099 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc()); 1100 return; 1101 } 1102 1103 // Compute the truncated division result in output64. 1104 Divide64WithConstant(masm, ins); 1105 1106 // Compute the remainder: output = lhs - (output * rhs). 1107 { 1108 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 1109 ARMRegister rhs64 = temps.AcquireX(); 1110 1111 masm.Mov(rhs64, d); 1112 masm.Msub(output64, output64, rhs64, lhs64); 1113 } 1114 } 1115 1116 void CodeGenerator::visitUModConstantI64(LUModConstantI64* ins) { 1117 ARMRegister lhs64 = toXRegister(ins->numerator()); 1118 ARMRegister output64 = toXRegister(ins->output()); 1119 1120 uint64_t d = ins->denominator(); 1121 1122 if (d == 0) { 1123 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc()); 1124 return; 1125 } 1126 1127 // Compute the truncated division result in output64. 1128 UnsignedDivide64WithConstant(masm, ins); 1129 1130 // Compute the remainder: output = lhs - (output * rhs). 1131 { 1132 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 1133 ARMRegister rhs64 = temps.AcquireX(); 1134 1135 masm.Mov(rhs64, d); 1136 masm.Msub(output64, output64, rhs64, lhs64); 1137 } 1138 } 1139 1140 void CodeGeneratorARM64::emitBigIntPtrDiv(LBigIntPtrDiv* ins, Register dividend, 1141 Register divisor, Register output) { 1142 // Callers handle division by zero and integer overflow. 1143 1144 const ARMRegister dividend64(dividend, 64); 1145 const ARMRegister divisor64(divisor, 64); 1146 const ARMRegister output64(output, 64); 1147 1148 masm.Sdiv(/* result= */ output64, dividend64, divisor64); 1149 } 1150 1151 void CodeGeneratorARM64::emitBigIntPtrMod(LBigIntPtrMod* ins, Register dividend, 1152 Register divisor, Register output) { 1153 // Callers handle division by zero and integer overflow. 1154 1155 const ARMRegister dividend64(dividend, 64); 1156 const ARMRegister divisor64(divisor, 64); 1157 const ARMRegister output64(output, 64); 1158 1159 // Signed division. 1160 masm.Sdiv(output64, dividend64, divisor64); 1161 1162 // Compute the remainder: output = dividend - (output * divisor). 1163 masm.Msub(/* result= */ output64, output64, divisor64, dividend64); 1164 } 1165 1166 void CodeGenerator::visitBitNotI(LBitNotI* ins) { 1167 const LAllocation* input = ins->input(); 1168 const LDefinition* output = ins->output(); 1169 masm.Mvn(toWRegister(output), toWOperand(input)); 1170 } 1171 1172 void CodeGenerator::visitBitNotI64(LBitNotI64* ins) { 1173 Register64 input = ToRegister64(ins->input()); 1174 Register64 output = ToOutRegister64(ins); 1175 masm.Mvn(vixl::Register(output.reg, 64), vixl::Register(input.reg, 64)); 1176 } 1177 1178 void CodeGenerator::visitBitOpI(LBitOpI* ins) { 1179 const ARMRegister lhs = toWRegister(ins->lhs()); 1180 const Operand rhs = toWOperand(ins->rhs()); 1181 const ARMRegister dest = toWRegister(ins->output()); 1182 1183 switch (ins->bitop()) { 1184 case JSOp::BitOr: 1185 masm.Orr(dest, lhs, rhs); 1186 break; 1187 case JSOp::BitXor: 1188 masm.Eor(dest, lhs, rhs); 1189 break; 1190 case JSOp::BitAnd: 1191 masm.And(dest, lhs, rhs); 1192 break; 1193 default: 1194 MOZ_CRASH("unexpected binary opcode"); 1195 } 1196 } 1197 1198 void CodeGenerator::visitShiftI(LShiftI* ins) { 1199 const ARMRegister lhs = toWRegister(ins->lhs()); 1200 const LAllocation* rhs = ins->rhs(); 1201 const ARMRegister dest = toWRegister(ins->output()); 1202 1203 if (rhs->isConstant()) { 1204 int32_t shift = ToInt32(rhs) & 0x1F; 1205 switch (ins->bitop()) { 1206 case JSOp::Lsh: 1207 if (shift) { 1208 masm.Lsl(dest, lhs, shift); 1209 } else { 1210 masm.Mov(dest, lhs); 1211 } 1212 break; 1213 case JSOp::Rsh: 1214 if (shift) { 1215 masm.Asr(dest, lhs, shift); 1216 } else { 1217 masm.Mov(dest, lhs); 1218 } 1219 break; 1220 case JSOp::Ursh: 1221 if (shift) { 1222 masm.Lsr(dest, lhs, shift); 1223 } else if (ins->mir()->toUrsh()->fallible()) { 1224 // x >>> 0 can overflow. 1225 masm.Ands(dest, lhs, Operand(0xFFFFFFFF)); 1226 bailoutIf(Assembler::Signed, ins->snapshot()); 1227 } else { 1228 masm.Mov(dest, lhs); 1229 } 1230 break; 1231 default: 1232 MOZ_CRASH("Unexpected shift op"); 1233 } 1234 } else { 1235 const ARMRegister rhsreg = toWRegister(rhs); 1236 switch (ins->bitop()) { 1237 case JSOp::Lsh: 1238 masm.Lsl(dest, lhs, rhsreg); 1239 break; 1240 case JSOp::Rsh: 1241 masm.Asr(dest, lhs, rhsreg); 1242 break; 1243 case JSOp::Ursh: 1244 masm.Lsr(dest, lhs, rhsreg); 1245 if (ins->mir()->toUrsh()->fallible()) { 1246 /// x >>> 0 can overflow. 1247 masm.Cmp(dest, Operand(0)); 1248 bailoutIf(Assembler::LessThan, ins->snapshot()); 1249 } 1250 break; 1251 default: 1252 MOZ_CRASH("Unexpected shift op"); 1253 } 1254 } 1255 } 1256 1257 void CodeGenerator::visitShiftIntPtr(LShiftIntPtr* ins) { 1258 ARMRegister lhs = toXRegister(ins->lhs()); 1259 const LAllocation* rhs = ins->rhs(); 1260 ARMRegister dest = toXRegister(ins->output()); 1261 1262 if (rhs->isConstant()) { 1263 int32_t shift = ToIntPtr(rhs) & 0x3F; 1264 if (shift == 0) { 1265 masm.Mov(dest, lhs); 1266 } else { 1267 switch (ins->bitop()) { 1268 case JSOp::Lsh: 1269 masm.Lsl(dest, lhs, shift); 1270 break; 1271 case JSOp::Rsh: 1272 masm.Asr(dest, lhs, shift); 1273 break; 1274 case JSOp::Ursh: 1275 masm.Lsr(dest, lhs, shift); 1276 break; 1277 default: 1278 MOZ_CRASH("Unexpected shift op"); 1279 } 1280 } 1281 } else { 1282 ARMRegister rhsreg = toXRegister(rhs); 1283 switch (ins->bitop()) { 1284 case JSOp::Lsh: 1285 masm.Lsl(dest, lhs, rhsreg); 1286 break; 1287 case JSOp::Rsh: 1288 masm.Asr(dest, lhs, rhsreg); 1289 break; 1290 case JSOp::Ursh: 1291 masm.Lsr(dest, lhs, rhsreg); 1292 break; 1293 default: 1294 MOZ_CRASH("Unexpected shift op"); 1295 } 1296 } 1297 } 1298 1299 void CodeGenerator::visitUrshD(LUrshD* ins) { 1300 ARMRegister lhs = toWRegister(ins->lhs()); 1301 const LAllocation* rhs = ins->rhs(); 1302 FloatRegister out = ToFloatRegister(ins->output()); 1303 ARMRegister temp = toWRegister(ins->temp0()); 1304 1305 if (rhs->isConstant()) { 1306 int32_t shift = ToInt32(rhs) & 0x1F; 1307 if (shift) { 1308 masm.Lsr(temp, lhs, shift); 1309 masm.convertUInt32ToDouble(temp.asUnsized(), out); 1310 } else { 1311 masm.convertUInt32ToDouble(lhs.asUnsized(), out); 1312 } 1313 } else { 1314 masm.Lsr(temp, lhs, toWRegister(rhs)); 1315 masm.convertUInt32ToDouble(temp.asUnsized(), out); 1316 } 1317 } 1318 1319 void CodeGenerator::visitPowHalfD(LPowHalfD* ins) { 1320 FloatRegister input = ToFloatRegister(ins->input()); 1321 FloatRegister output = ToFloatRegister(ins->output()); 1322 1323 ScratchDoubleScope scratch(masm); 1324 1325 Label done, sqrt; 1326 1327 if (!ins->mir()->operandIsNeverNegativeInfinity()) { 1328 // Branch if not -Infinity. 1329 masm.loadConstantDouble(NegativeInfinity<double>(), scratch); 1330 1331 Assembler::DoubleCondition cond = Assembler::DoubleNotEqualOrUnordered; 1332 if (ins->mir()->operandIsNeverNaN()) { 1333 cond = Assembler::DoubleNotEqual; 1334 } 1335 masm.branchDouble(cond, input, scratch, &sqrt); 1336 1337 // Math.pow(-Infinity, 0.5) == Infinity. 1338 masm.Fneg(ARMFPRegister(output, 64), ARMFPRegister(scratch, 64)); 1339 masm.jump(&done); 1340 1341 masm.bind(&sqrt); 1342 } 1343 1344 if (!ins->mir()->operandIsNeverNegativeZero()) { 1345 // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). 1346 // Adding 0 converts any -0 to 0. 1347 masm.zeroDouble(scratch); 1348 masm.addDouble(input, scratch); 1349 masm.sqrtDouble(scratch, output); 1350 } else { 1351 masm.sqrtDouble(input, output); 1352 } 1353 1354 masm.bind(&done); 1355 } 1356 1357 MoveOperand CodeGeneratorARM64::toMoveOperand(const LAllocation a) const { 1358 if (a.isGeneralReg()) { 1359 return MoveOperand(ToRegister(a)); 1360 } 1361 if (a.isFloatReg()) { 1362 return MoveOperand(ToFloatRegister(a)); 1363 } 1364 MoveOperand::Kind kind = a.isStackArea() ? MoveOperand::Kind::EffectiveAddress 1365 : MoveOperand::Kind::Memory; 1366 return MoveOperand(ToAddress(a), kind); 1367 } 1368 1369 class js::jit::OutOfLineTableSwitch 1370 : public OutOfLineCodeBase<CodeGeneratorARM64> { 1371 MTableSwitch* mir_; 1372 CodeLabel jumpLabel_; 1373 1374 void accept(CodeGeneratorARM64* codegen) override { 1375 codegen->visitOutOfLineTableSwitch(this); 1376 } 1377 1378 public: 1379 explicit OutOfLineTableSwitch(MTableSwitch* mir) : mir_(mir) {} 1380 1381 MTableSwitch* mir() const { return mir_; } 1382 1383 CodeLabel* jumpLabel() { return &jumpLabel_; } 1384 }; 1385 1386 void CodeGeneratorARM64::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool) { 1387 MTableSwitch* mir = ool->mir(); 1388 1389 // Prevent nop and pools sequences to appear in the jump table. 1390 AutoForbidPoolsAndNops afp( 1391 &masm, (mir->numCases() + 1) * (sizeof(void*) / vixl::kInstructionSize)); 1392 masm.haltingAlign(sizeof(void*)); 1393 masm.bind(ool->jumpLabel()); 1394 masm.addCodeLabel(*ool->jumpLabel()); 1395 1396 for (size_t i = 0; i < mir->numCases(); i++) { 1397 LBlock* caseblock = skipTrivialBlocks(mir->getCase(i))->lir(); 1398 Label* caseheader = caseblock->label(); 1399 uint32_t caseoffset = caseheader->offset(); 1400 1401 // The entries of the jump table need to be absolute addresses, 1402 // and thus must be patched after codegen is finished. 1403 CodeLabel cl; 1404 masm.writeCodePointer(&cl); 1405 cl.target()->bind(caseoffset); 1406 masm.addCodeLabel(cl); 1407 } 1408 } 1409 1410 void CodeGeneratorARM64::emitTableSwitchDispatch(MTableSwitch* mir, 1411 Register index, 1412 Register base) { 1413 Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label(); 1414 1415 // Let the lowest table entry be indexed at 0. 1416 if (mir->low() != 0) { 1417 masm.sub32(Imm32(mir->low()), index); 1418 } 1419 1420 // Jump to the default case if input is out of range. 1421 int32_t cases = mir->numCases(); 1422 masm.branch32(Assembler::AboveOrEqual, index, Imm32(cases), defaultcase); 1423 1424 // Because the target code has not yet been generated, we cannot know the 1425 // instruction offsets for use as jump targets. Therefore we construct 1426 // an OutOfLineTableSwitch that winds up holding the jump table. 1427 // 1428 // Because the jump table is generated as part of out-of-line code, 1429 // it is generated after all the regular codegen, so the jump targets 1430 // are guaranteed to exist when generating the jump table. 1431 OutOfLineTableSwitch* ool = new (alloc()) OutOfLineTableSwitch(mir); 1432 addOutOfLineCode(ool, mir); 1433 1434 // Use the index to get the address of the jump target from the table. 1435 masm.mov(ool->jumpLabel(), base); 1436 BaseIndex pointer(base, index, ScalePointer); 1437 1438 // Load the target from the jump table and branch to it. 1439 masm.branchToComputedAddress(pointer); 1440 } 1441 1442 void CodeGenerator::visitMathD(LMathD* math) { 1443 ARMFPRegister lhs(ToFloatRegister(math->lhs()), 64); 1444 ARMFPRegister rhs(ToFloatRegister(math->rhs()), 64); 1445 ARMFPRegister output(ToFloatRegister(math->output()), 64); 1446 1447 switch (math->jsop()) { 1448 case JSOp::Add: 1449 masm.Fadd(output, lhs, rhs); 1450 break; 1451 case JSOp::Sub: 1452 masm.Fsub(output, lhs, rhs); 1453 break; 1454 case JSOp::Mul: 1455 masm.Fmul(output, lhs, rhs); 1456 break; 1457 case JSOp::Div: 1458 masm.Fdiv(output, lhs, rhs); 1459 break; 1460 default: 1461 MOZ_CRASH("unexpected opcode"); 1462 } 1463 } 1464 1465 void CodeGenerator::visitMathF(LMathF* math) { 1466 ARMFPRegister lhs(ToFloatRegister(math->lhs()), 32); 1467 ARMFPRegister rhs(ToFloatRegister(math->rhs()), 32); 1468 ARMFPRegister output(ToFloatRegister(math->output()), 32); 1469 1470 switch (math->jsop()) { 1471 case JSOp::Add: 1472 masm.Fadd(output, lhs, rhs); 1473 break; 1474 case JSOp::Sub: 1475 masm.Fsub(output, lhs, rhs); 1476 break; 1477 case JSOp::Mul: 1478 masm.Fmul(output, lhs, rhs); 1479 break; 1480 case JSOp::Div: 1481 masm.Fdiv(output, lhs, rhs); 1482 break; 1483 default: 1484 MOZ_CRASH("unexpected opcode"); 1485 } 1486 } 1487 1488 void CodeGenerator::visitTruncateDToInt32(LTruncateDToInt32* ins) { 1489 FloatRegister input = ToFloatRegister(ins->input()); 1490 Register output = ToRegister(ins->output()); 1491 1492 // Directly call Fjcvtzs if available to avoid generating unused OOL code in 1493 // emitTruncateDouble. 1494 if (masm.hasFjcvtzs()) { 1495 masm.Fjcvtzs(ARMRegister(output, 32), ARMFPRegister(input, 64)); 1496 } else { 1497 emitTruncateDouble(input, output, ins->mir()); 1498 } 1499 } 1500 1501 void CodeGenerator::visitWasmBuiltinTruncateDToInt32( 1502 LWasmBuiltinTruncateDToInt32* lir) { 1503 FloatRegister input = ToFloatRegister(lir->input()); 1504 Register output = ToRegister(lir->output()); 1505 1506 // Directly call Fjcvtzs if available to avoid generating unused OOL code in 1507 // emitTruncateDouble. 1508 if (masm.hasFjcvtzs()) { 1509 masm.Fjcvtzs(ARMRegister(output, 32), ARMFPRegister(input, 64)); 1510 } else { 1511 emitTruncateDouble(input, output, lir->mir()); 1512 } 1513 } 1514 1515 void CodeGenerator::visitTruncateFToInt32(LTruncateFToInt32* ins) { 1516 masm.truncateFloat32ModUint32(ToFloatRegister(ins->input()), 1517 ToRegister(ins->output())); 1518 } 1519 1520 void CodeGenerator::visitWasmBuiltinTruncateFToInt32( 1521 LWasmBuiltinTruncateFToInt32* lir) { 1522 MOZ_ASSERT(lir->instance()->isBogus(), "instance not used for arm64"); 1523 masm.truncateFloat32ModUint32(ToFloatRegister(lir->input()), 1524 ToRegister(lir->output())); 1525 } 1526 1527 void CodeGenerator::visitBox(LBox* box) { 1528 const LAllocation* in = box->payload(); 1529 ValueOperand result = ToOutValue(box); 1530 1531 masm.moveValue(TypedOrValueRegister(box->type(), ToAnyRegister(in)), result); 1532 } 1533 1534 void CodeGenerator::visitUnbox(LUnbox* unbox) { 1535 MUnbox* mir = unbox->mir(); 1536 1537 Register result = ToRegister(unbox->output()); 1538 1539 if (mir->fallible()) { 1540 ValueOperand value = ToValue(unbox->input()); 1541 Label bail; 1542 switch (mir->type()) { 1543 case MIRType::Int32: 1544 masm.fallibleUnboxInt32(value, result, &bail); 1545 break; 1546 case MIRType::Boolean: 1547 masm.fallibleUnboxBoolean(value, result, &bail); 1548 break; 1549 case MIRType::Object: 1550 masm.fallibleUnboxObject(value, result, &bail); 1551 break; 1552 case MIRType::String: 1553 masm.fallibleUnboxString(value, result, &bail); 1554 break; 1555 case MIRType::Symbol: 1556 masm.fallibleUnboxSymbol(value, result, &bail); 1557 break; 1558 case MIRType::BigInt: 1559 masm.fallibleUnboxBigInt(value, result, &bail); 1560 break; 1561 default: 1562 MOZ_CRASH("Given MIRType cannot be unboxed."); 1563 } 1564 bailoutFrom(&bail, unbox->snapshot()); 1565 return; 1566 } 1567 1568 // Infallible unbox. 1569 1570 ValueOperand input = ToValue(unbox->input()); 1571 1572 #ifdef DEBUG 1573 // Assert the types match. 1574 JSValueTag tag = MIRTypeToTag(mir->type()); 1575 Label ok; 1576 { 1577 ScratchTagScope scratch(masm, input); 1578 masm.splitTagForTest(input, scratch); 1579 masm.cmpTag(scratch, ImmTag(tag)); 1580 } 1581 masm.B(&ok, Assembler::Condition::Equal); 1582 masm.assumeUnreachable("Infallible unbox type mismatch"); 1583 masm.bind(&ok); 1584 #endif 1585 1586 switch (mir->type()) { 1587 case MIRType::Int32: 1588 masm.unboxInt32(input, result); 1589 break; 1590 case MIRType::Boolean: 1591 masm.unboxBoolean(input, result); 1592 break; 1593 case MIRType::Object: 1594 masm.unboxObject(input, result); 1595 break; 1596 case MIRType::String: 1597 masm.unboxString(input, result); 1598 break; 1599 case MIRType::Symbol: 1600 masm.unboxSymbol(input, result); 1601 break; 1602 case MIRType::BigInt: 1603 masm.unboxBigInt(input, result); 1604 break; 1605 default: 1606 MOZ_CRASH("Given MIRType cannot be unboxed."); 1607 } 1608 } 1609 1610 void CodeGenerator::visitTestDAndBranch(LTestDAndBranch* test) { 1611 const LAllocation* opd = test->input(); 1612 MBasicBlock* ifTrue = test->ifTrue(); 1613 MBasicBlock* ifFalse = test->ifFalse(); 1614 1615 masm.Fcmp(ARMFPRegister(ToFloatRegister(opd), 64), 0.0); 1616 1617 // If the compare set the 0 bit, then the result is definitely false. 1618 jumpToBlock(ifFalse, Assembler::Zero); 1619 1620 // Overflow means one of the operands was NaN, which is also false. 1621 jumpToBlock(ifFalse, Assembler::Overflow); 1622 jumpToBlock(ifTrue); 1623 } 1624 1625 void CodeGenerator::visitTestFAndBranch(LTestFAndBranch* test) { 1626 const LAllocation* opd = test->input(); 1627 MBasicBlock* ifTrue = test->ifTrue(); 1628 MBasicBlock* ifFalse = test->ifFalse(); 1629 1630 masm.Fcmp(ARMFPRegister(ToFloatRegister(opd), 32), 0.0); 1631 1632 // If the compare set the 0 bit, then the result is definitely false. 1633 jumpToBlock(ifFalse, Assembler::Zero); 1634 1635 // Overflow means one of the operands was NaN, which is also false. 1636 jumpToBlock(ifFalse, Assembler::Overflow); 1637 jumpToBlock(ifTrue); 1638 } 1639 1640 void CodeGenerator::visitCompareD(LCompareD* comp) { 1641 const FloatRegister left = ToFloatRegister(comp->left()); 1642 const FloatRegister right = ToFloatRegister(comp->right()); 1643 ARMRegister output = toWRegister(comp->output()); 1644 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); 1645 1646 masm.compareDouble(left, right); 1647 masm.cset(output, Assembler::ConditionFromDoubleCondition(cond)); 1648 } 1649 1650 void CodeGenerator::visitCompareF(LCompareF* comp) { 1651 const FloatRegister left = ToFloatRegister(comp->left()); 1652 const FloatRegister right = ToFloatRegister(comp->right()); 1653 ARMRegister output = toWRegister(comp->output()); 1654 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); 1655 1656 masm.compareFloat(left, right); 1657 masm.cset(output, Assembler::ConditionFromDoubleCondition(cond)); 1658 } 1659 1660 void CodeGenerator::visitCompareDAndBranch(LCompareDAndBranch* comp) { 1661 const FloatRegister left = ToFloatRegister(comp->left()); 1662 const FloatRegister right = ToFloatRegister(comp->right()); 1663 Assembler::DoubleCondition doubleCond = 1664 JSOpToDoubleCondition(comp->cmpMir()->jsop()); 1665 Assembler::Condition cond = 1666 Assembler::ConditionFromDoubleCondition(doubleCond); 1667 1668 masm.compareDouble(left, right); 1669 emitBranch(cond, comp->ifTrue(), comp->ifFalse()); 1670 } 1671 1672 void CodeGenerator::visitCompareFAndBranch(LCompareFAndBranch* comp) { 1673 const FloatRegister left = ToFloatRegister(comp->left()); 1674 const FloatRegister right = ToFloatRegister(comp->right()); 1675 Assembler::DoubleCondition doubleCond = 1676 JSOpToDoubleCondition(comp->cmpMir()->jsop()); 1677 Assembler::Condition cond = 1678 Assembler::ConditionFromDoubleCondition(doubleCond); 1679 1680 masm.compareFloat(left, right); 1681 emitBranch(cond, comp->ifTrue(), comp->ifFalse()); 1682 } 1683 1684 void CodeGenerator::visitWasmUint32ToDouble(LWasmUint32ToDouble* lir) { 1685 masm.convertUInt32ToDouble(ToRegister(lir->input()), 1686 ToFloatRegister(lir->output())); 1687 } 1688 1689 void CodeGenerator::visitWasmUint32ToFloat32(LWasmUint32ToFloat32* lir) { 1690 masm.convertUInt32ToFloat32(ToRegister(lir->input()), 1691 ToFloatRegister(lir->output())); 1692 } 1693 1694 // NZCV 1695 // NAN -> 0011 1696 // == -> 0110 1697 // < -> 1000 1698 // > -> 0010 1699 void CodeGenerator::visitNotD(LNotD* ins) { 1700 ARMFPRegister input(ToFloatRegister(ins->input()), 64); 1701 ARMRegister output = toWRegister(ins->output()); 1702 1703 // Set output to 1 if input compares equal to 0.0, else 0. 1704 masm.Fcmp(input, 0.0); 1705 masm.Cset(output, Assembler::Equal); 1706 1707 // Comparison with NaN sets V in the NZCV register. 1708 // If the input was NaN, output must now be zero, so it can be incremented. 1709 // The instruction is read: "output = if NoOverflow then output else 0+1". 1710 masm.Csinc(output, output, ZeroRegister32, Assembler::NoOverflow); 1711 } 1712 1713 void CodeGenerator::visitNotF(LNotF* ins) { 1714 ARMFPRegister input(ToFloatRegister(ins->input()), 32); 1715 ARMRegister output = toWRegister(ins->output()); 1716 1717 // Set output to 1 input compares equal to 0.0, else 0. 1718 masm.Fcmp(input, 0.0); 1719 masm.Cset(output, Assembler::Equal); 1720 1721 // Comparison with NaN sets V in the NZCV register. 1722 // If the input was NaN, output must now be zero, so it can be incremented. 1723 // The instruction is read: "output = if NoOverflow then output else 0+1". 1724 masm.Csinc(output, output, ZeroRegister32, Assembler::NoOverflow); 1725 } 1726 1727 void CodeGeneratorARM64::generateInvalidateEpilogue() { 1728 // Ensure that there is enough space in the buffer for the OsiPoint patching 1729 // to occur. Otherwise, we could overwrite the invalidation epilogue. 1730 for (size_t i = 0; i < sizeof(void*); i += Assembler::NopSize()) { 1731 masm.nop(); 1732 } 1733 1734 masm.bind(&invalidate_); 1735 1736 // Push the return address of the point that we bailout out onto the stack. 1737 masm.push(lr); 1738 1739 // Push the Ion script onto the stack (when we determine what that pointer 1740 // is). 1741 invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1))); 1742 1743 // Jump to the invalidator which will replace the current frame. 1744 TrampolinePtr thunk = gen->jitRuntime()->getInvalidationThunk(); 1745 masm.jump(thunk); 1746 } 1747 1748 template <class U> 1749 Register getBase(U* mir) { 1750 switch (mir->base()) { 1751 case U::Heap: 1752 return HeapReg; 1753 } 1754 return InvalidReg; 1755 } 1756 1757 void CodeGenerator::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) { 1758 const MAsmJSLoadHeap* mir = ins->mir(); 1759 MOZ_ASSERT(!mir->hasMemoryBase()); 1760 1761 const LAllocation* ptr = ins->ptr(); 1762 const LAllocation* boundsCheckLimit = ins->boundsCheckLimit(); 1763 1764 Register ptrReg = ToRegister(ptr); 1765 Scalar::Type accessType = mir->accessType(); 1766 bool isFloat = accessType == Scalar::Float32 || accessType == Scalar::Float64; 1767 Label done; 1768 1769 if (mir->needsBoundsCheck()) { 1770 Label boundsCheckPassed; 1771 Register boundsCheckLimitReg = ToRegister(boundsCheckLimit); 1772 masm.wasmBoundsCheck32(Assembler::Below, ptrReg, boundsCheckLimitReg, 1773 &boundsCheckPassed); 1774 // Return a default value in case of a bounds-check failure. 1775 if (isFloat) { 1776 if (accessType == Scalar::Float32) { 1777 masm.loadConstantFloat32(GenericNaN(), ToFloatRegister(ins->output())); 1778 } else { 1779 masm.loadConstantDouble(GenericNaN(), ToFloatRegister(ins->output())); 1780 } 1781 } else { 1782 masm.Mov(ARMRegister(ToRegister(ins->output()), 64), 0); 1783 } 1784 masm.jump(&done); 1785 masm.bind(&boundsCheckPassed); 1786 } 1787 1788 MemOperand addr(ARMRegister(HeapReg, 64), ARMRegister(ptrReg, 64)); 1789 switch (accessType) { 1790 case Scalar::Int8: 1791 masm.Ldrb(toWRegister(ins->output()), addr); 1792 masm.Sxtb(toWRegister(ins->output()), toWRegister(ins->output())); 1793 break; 1794 case Scalar::Uint8: 1795 masm.Ldrb(toWRegister(ins->output()), addr); 1796 break; 1797 case Scalar::Int16: 1798 masm.Ldrh(toWRegister(ins->output()), addr); 1799 masm.Sxth(toWRegister(ins->output()), toWRegister(ins->output())); 1800 break; 1801 case Scalar::Uint16: 1802 masm.Ldrh(toWRegister(ins->output()), addr); 1803 break; 1804 case Scalar::Int32: 1805 case Scalar::Uint32: 1806 masm.Ldr(toWRegister(ins->output()), addr); 1807 break; 1808 case Scalar::Float64: 1809 masm.Ldr(ARMFPRegister(ToFloatRegister(ins->output()), 64), addr); 1810 break; 1811 case Scalar::Float32: 1812 masm.Ldr(ARMFPRegister(ToFloatRegister(ins->output()), 32), addr); 1813 break; 1814 default: 1815 MOZ_CRASH("unexpected array type"); 1816 } 1817 if (done.used()) { 1818 masm.bind(&done); 1819 } 1820 } 1821 1822 void CodeGenerator::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) { 1823 const MAsmJSStoreHeap* mir = ins->mir(); 1824 MOZ_ASSERT(!mir->hasMemoryBase()); 1825 1826 const LAllocation* ptr = ins->ptr(); 1827 const LAllocation* boundsCheckLimit = ins->boundsCheckLimit(); 1828 1829 Register ptrReg = ToRegister(ptr); 1830 1831 Label done; 1832 if (mir->needsBoundsCheck()) { 1833 Register boundsCheckLimitReg = ToRegister(boundsCheckLimit); 1834 masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptrReg, boundsCheckLimitReg, 1835 &done); 1836 } 1837 1838 MemOperand addr(ARMRegister(HeapReg, 64), ARMRegister(ptrReg, 64)); 1839 switch (mir->accessType()) { 1840 case Scalar::Int8: 1841 case Scalar::Uint8: 1842 masm.Strb(toWRegister(ins->value()), addr); 1843 break; 1844 case Scalar::Int16: 1845 case Scalar::Uint16: 1846 masm.Strh(toWRegister(ins->value()), addr); 1847 break; 1848 case Scalar::Int32: 1849 case Scalar::Uint32: 1850 masm.Str(toWRegister(ins->value()), addr); 1851 break; 1852 case Scalar::Float64: 1853 masm.Str(ARMFPRegister(ToFloatRegister(ins->value()), 64), addr); 1854 break; 1855 case Scalar::Float32: 1856 masm.Str(ARMFPRegister(ToFloatRegister(ins->value()), 32), addr); 1857 break; 1858 default: 1859 MOZ_CRASH("unexpected array type"); 1860 } 1861 if (done.used()) { 1862 masm.bind(&done); 1863 } 1864 } 1865 1866 void CodeGenerator::visitWasmCompareExchangeHeap( 1867 LWasmCompareExchangeHeap* ins) { 1868 MWasmCompareExchangeHeap* mir = ins->mir(); 1869 1870 Register memoryBase = ToRegister(ins->memoryBase()); 1871 Register ptr = ToRegister(ins->ptr()); 1872 Register oldval = ToRegister(ins->oldValue()); 1873 Register newval = ToRegister(ins->newValue()); 1874 Register out = ToRegister(ins->output()); 1875 1876 BaseIndex srcAddr(memoryBase, ptr, TimesOne, mir->access().offset32()); 1877 1878 if (mir->access().type() == Scalar::Int64) { 1879 masm.wasmCompareExchange64(mir->access(), srcAddr, Register64(oldval), 1880 Register64(newval), Register64(out)); 1881 } else { 1882 masm.wasmCompareExchange(mir->access(), srcAddr, oldval, newval, out); 1883 } 1884 } 1885 1886 void CodeGenerator::visitWasmAtomicExchangeHeap(LWasmAtomicExchangeHeap* ins) { 1887 MWasmAtomicExchangeHeap* mir = ins->mir(); 1888 1889 Register memoryBase = ToRegister(ins->memoryBase()); 1890 Register ptr = ToRegister(ins->ptr()); 1891 Register oldval = ToRegister(ins->value()); 1892 Register out = ToRegister(ins->output()); 1893 1894 BaseIndex srcAddr(memoryBase, ptr, TimesOne, mir->access().offset32()); 1895 1896 if (mir->access().type() == Scalar::Int64) { 1897 masm.wasmAtomicExchange64(mir->access(), srcAddr, Register64(oldval), 1898 Register64(out)); 1899 } else { 1900 masm.wasmAtomicExchange(mir->access(), srcAddr, oldval, out); 1901 } 1902 } 1903 1904 void CodeGenerator::visitWasmAtomicBinopHeap(LWasmAtomicBinopHeap* ins) { 1905 MWasmAtomicBinopHeap* mir = ins->mir(); 1906 1907 MOZ_ASSERT(mir->hasUses()); 1908 1909 Register memoryBase = ToRegister(ins->memoryBase()); 1910 Register ptr = ToRegister(ins->ptr()); 1911 Register value = ToRegister(ins->value()); 1912 Register flagTemp = ToRegister(ins->temp0()); 1913 Register out = ToRegister(ins->output()); 1914 1915 BaseIndex srcAddr(memoryBase, ptr, TimesOne, mir->access().offset32()); 1916 AtomicOp op = mir->operation(); 1917 1918 if (mir->access().type() == Scalar::Int64) { 1919 masm.wasmAtomicFetchOp64(mir->access(), op, Register64(value), srcAddr, 1920 Register64(flagTemp), Register64(out)); 1921 } else { 1922 masm.wasmAtomicFetchOp(mir->access(), op, value, srcAddr, flagTemp, out); 1923 } 1924 } 1925 1926 void CodeGenerator::visitWasmAtomicBinopHeapForEffect( 1927 LWasmAtomicBinopHeapForEffect* ins) { 1928 MWasmAtomicBinopHeap* mir = ins->mir(); 1929 1930 MOZ_ASSERT(!mir->hasUses()); 1931 1932 Register memoryBase = ToRegister(ins->memoryBase()); 1933 Register ptr = ToRegister(ins->ptr()); 1934 Register value = ToRegister(ins->value()); 1935 Register flagTemp = ToRegister(ins->temp0()); 1936 1937 BaseIndex srcAddr(memoryBase, ptr, TimesOne, mir->access().offset32()); 1938 AtomicOp op = mir->operation(); 1939 1940 if (mir->access().type() == Scalar::Int64) { 1941 masm.wasmAtomicEffectOp64(mir->access(), op, Register64(value), srcAddr, 1942 Register64(flagTemp)); 1943 } else { 1944 masm.wasmAtomicEffectOp(mir->access(), op, value, srcAddr, flagTemp); 1945 } 1946 } 1947 1948 void CodeGenerator::visitWasmStackArg(LWasmStackArg* ins) { 1949 const MWasmStackArg* mir = ins->mir(); 1950 Address dst(masm.getStackPointer(), mir->spOffset()); 1951 if (ins->arg()->isConstant()) { 1952 masm.storePtr(ImmWord(ToInt32(ins->arg())), dst); 1953 } else if (ins->arg()->isGeneralReg()) { 1954 masm.storePtr(ToRegister(ins->arg()), dst); 1955 } else { 1956 switch (mir->input()->type()) { 1957 case MIRType::Double: 1958 masm.storeDouble(ToFloatRegister(ins->arg()), dst); 1959 return; 1960 case MIRType::Float32: 1961 masm.storeFloat32(ToFloatRegister(ins->arg()), dst); 1962 return; 1963 #ifdef ENABLE_WASM_SIMD 1964 case MIRType::Simd128: 1965 masm.storeUnalignedSimd128(ToFloatRegister(ins->arg()), dst); 1966 return; 1967 #endif 1968 default: 1969 break; 1970 } 1971 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE( 1972 "unexpected mir type in WasmStackArg"); 1973 } 1974 } 1975 1976 void CodeGenerator::visitUDiv(LUDiv* ins) { 1977 MDiv* mir = ins->mir(); 1978 Register lhs = ToRegister(ins->lhs()); 1979 Register rhs = ToRegister(ins->rhs()); 1980 Register output = ToRegister(ins->output()); 1981 ARMRegister lhs32 = ARMRegister(lhs, 32); 1982 ARMRegister rhs32 = ARMRegister(rhs, 32); 1983 ARMRegister output32 = ARMRegister(output, 32); 1984 1985 // Prevent divide by zero. 1986 if (mir->canBeDivideByZero()) { 1987 if (mir->trapOnError()) { 1988 TrapIfDivideByZero(masm, ins, rhs32); 1989 } else if (mir->canTruncateInfinities()) { 1990 // Udiv returns zero for division by zero, exactly what we want for 1991 // truncated division. Remainder computation expects a non-zero divisor, 1992 // so we must also be allowed to truncate the remainder. 1993 MOZ_ASSERT(mir->canTruncateRemainder(), 1994 "remainder computation expects a non-zero divisor"); 1995 } else { 1996 MOZ_ASSERT(mir->fallible()); 1997 bailoutTest32(Assembler::Zero, rhs, rhs, ins->snapshot()); 1998 } 1999 } 2000 2001 // Unsigned division. 2002 masm.Udiv(output32, lhs32, rhs32); 2003 2004 // If the remainder is > 0, bailout since this must be a double. 2005 if (!mir->canTruncateRemainder()) { 2006 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 2007 ARMRegister remainder32 = temps.AcquireW(); 2008 Register remainder = remainder32.asUnsized(); 2009 2010 // Compute the remainder: remainder = lhs - (output * rhs). 2011 masm.Msub(remainder32, output32, rhs32, lhs32); 2012 2013 bailoutTest32(Assembler::NonZero, remainder, remainder, ins->snapshot()); 2014 } 2015 2016 // Unsigned div can return a value that's not a signed int32. 2017 // If our users aren't expecting that, bail. 2018 if (!mir->isTruncated()) { 2019 bailoutTest32(Assembler::Signed, output, output, ins->snapshot()); 2020 } 2021 } 2022 2023 void CodeGenerator::visitUMod(LUMod* ins) { 2024 Register rhs = ToRegister(ins->rhs()); 2025 Register output = ToRegister(ins->output()); 2026 2027 ARMRegister lhs32 = toWRegister(ins->lhs()); 2028 ARMRegister rhs32 = toWRegister(ins->rhs()); 2029 ARMRegister output32 = toWRegister(ins->output()); 2030 Label done; 2031 2032 MMod* mir = ins->mir(); 2033 2034 if (mir->canBeDivideByZero()) { 2035 if (mir->trapOnError()) { 2036 TrapIfDivideByZero(masm, ins, rhs32); 2037 } else if (mir->isTruncated()) { 2038 // Truncated division by zero yields integer zero. 2039 masm.Mov(output32, wzr); 2040 masm.Cbz(rhs32, &done); 2041 } else { 2042 // Non-truncated division by zero produces a non-integer. 2043 bailoutTest32(Assembler::Zero, rhs, rhs, ins->snapshot()); 2044 } 2045 } 2046 2047 // Unsigned division. 2048 masm.Udiv(output32, lhs32, rhs32); 2049 2050 // Compute the remainder: output = lhs - (output * rhs). 2051 masm.Msub(output32, output32, rhs32, lhs32); 2052 2053 if (!mir->isTruncated()) { 2054 // Bail if the output would be negative. 2055 // 2056 // LUMod inputs may be Uint32, so care is taken to ensure the result 2057 // is not unexpectedly signed. 2058 bailoutCmp32(Assembler::LessThan, output, Imm32(0), ins->snapshot()); 2059 } 2060 2061 if (done.used()) { 2062 masm.bind(&done); 2063 } 2064 } 2065 2066 void CodeGenerator::visitEffectiveAddress3(LEffectiveAddress3* ins) { 2067 const MEffectiveAddress3* mir = ins->mir(); 2068 const ARMRegister base = toWRegister(ins->base()); 2069 const ARMRegister index = toWRegister(ins->index()); 2070 const ARMRegister output = toWRegister(ins->output()); 2071 2072 if (mir->scale() == Scale::TimesOne) { 2073 masm.Add(output, base, Operand(index)); 2074 } else { 2075 masm.Add(output, base, Operand(index, vixl::LSL, mir->scale())); 2076 } 2077 if (mir->displacement() != 0) { 2078 masm.Add(output, output, Operand(mir->displacement())); 2079 } 2080 } 2081 2082 void CodeGenerator::visitEffectiveAddress2(LEffectiveAddress2* ins) { 2083 const MEffectiveAddress2* mir = ins->mir(); 2084 const ARMRegister index = toWRegister(ins->index()); 2085 const ARMRegister output = toWRegister(ins->output()); 2086 // Ensured because the LIR's `index` input is not an AtStart variant. 2087 // If this ever fails, we'll need to generate a slower sequence the same 2088 // as ::visitEffectiveAddress, but with `base` being `wzr`. 2089 MOZ_RELEASE_ASSERT(output.code() != index.code()); 2090 2091 masm.Mov(output, mir->displacement()); 2092 masm.Add(output, output, Operand(index, vixl::LSL, mir->scale())); 2093 } 2094 2095 void CodeGenerator::visitNegI(LNegI* ins) { 2096 const ARMRegister input = toWRegister(ins->input()); 2097 const ARMRegister output = toWRegister(ins->output()); 2098 masm.Neg(output, input); 2099 } 2100 2101 void CodeGenerator::visitNegI64(LNegI64* ins) { 2102 const ARMRegister input = toXRegister(ins->input()); 2103 const ARMRegister output = toXRegister(ins->output()); 2104 masm.Neg(output, input); 2105 } 2106 2107 void CodeGenerator::visitNegD(LNegD* ins) { 2108 const ARMFPRegister input(ToFloatRegister(ins->input()), 64); 2109 const ARMFPRegister output(ToFloatRegister(ins->output()), 64); 2110 masm.Fneg(output, input); 2111 } 2112 2113 void CodeGenerator::visitNegF(LNegF* ins) { 2114 const ARMFPRegister input(ToFloatRegister(ins->input()), 32); 2115 const ARMFPRegister output(ToFloatRegister(ins->output()), 32); 2116 masm.Fneg(output, input); 2117 } 2118 2119 void CodeGenerator::visitCompareExchangeTypedArrayElement( 2120 LCompareExchangeTypedArrayElement* lir) { 2121 Register elements = ToRegister(lir->elements()); 2122 AnyRegister output = ToAnyRegister(lir->output()); 2123 Register temp = ToTempRegisterOrInvalid(lir->temp0()); 2124 2125 Register oldval = ToRegister(lir->oldval()); 2126 Register newval = ToRegister(lir->newval()); 2127 2128 Scalar::Type arrayType = lir->mir()->arrayType(); 2129 2130 auto dest = ToAddressOrBaseIndex(elements, lir->index(), arrayType); 2131 2132 dest.match([&](const auto& dest) { 2133 masm.compareExchangeJS(arrayType, Synchronization::Full(), dest, oldval, 2134 newval, temp, output); 2135 }); 2136 } 2137 2138 void CodeGenerator::visitAtomicExchangeTypedArrayElement( 2139 LAtomicExchangeTypedArrayElement* lir) { 2140 Register elements = ToRegister(lir->elements()); 2141 AnyRegister output = ToAnyRegister(lir->output()); 2142 Register temp = ToTempRegisterOrInvalid(lir->temp0()); 2143 2144 Register value = ToRegister(lir->value()); 2145 2146 Scalar::Type arrayType = lir->mir()->arrayType(); 2147 2148 auto dest = ToAddressOrBaseIndex(elements, lir->index(), arrayType); 2149 2150 dest.match([&](const auto& dest) { 2151 masm.atomicExchangeJS(arrayType, Synchronization::Full(), dest, value, temp, 2152 output); 2153 }); 2154 } 2155 2156 void CodeGenerator::visitAtomicLoad64(LAtomicLoad64* lir) { 2157 Register elements = ToRegister(lir->elements()); 2158 Register64 out = ToOutRegister64(lir); 2159 2160 Scalar::Type storageType = lir->mir()->storageType(); 2161 2162 auto source = ToAddressOrBaseIndex(elements, lir->index(), storageType); 2163 2164 // NOTE: the generated code must match the assembly code in gen_load in 2165 // GenerateAtomicOperations.py 2166 auto sync = Synchronization::Load(); 2167 2168 masm.memoryBarrierBefore(sync); 2169 source.match([&](const auto& source) { masm.load64(source, out); }); 2170 masm.memoryBarrierAfter(sync); 2171 } 2172 2173 void CodeGenerator::visitAtomicStore64(LAtomicStore64* lir) { 2174 Register elements = ToRegister(lir->elements()); 2175 Register64 value = ToRegister64(lir->value()); 2176 2177 Scalar::Type writeType = lir->mir()->writeType(); 2178 2179 auto dest = ToAddressOrBaseIndex(elements, lir->index(), writeType); 2180 2181 // NOTE: the generated code must match the assembly code in gen_store in 2182 // GenerateAtomicOperations.py 2183 auto sync = Synchronization::Store(); 2184 2185 masm.memoryBarrierBefore(sync); 2186 dest.match([&](const auto& dest) { masm.store64(value, dest); }); 2187 masm.memoryBarrierAfter(sync); 2188 } 2189 2190 void CodeGenerator::visitCompareExchangeTypedArrayElement64( 2191 LCompareExchangeTypedArrayElement64* lir) { 2192 Register elements = ToRegister(lir->elements()); 2193 Register64 oldval = ToRegister64(lir->oldval()); 2194 Register64 newval = ToRegister64(lir->newval()); 2195 Register64 out = ToOutRegister64(lir); 2196 2197 Scalar::Type arrayType = lir->mir()->arrayType(); 2198 2199 auto dest = ToAddressOrBaseIndex(elements, lir->index(), arrayType); 2200 2201 dest.match([&](const auto& dest) { 2202 masm.compareExchange64(Synchronization::Full(), dest, oldval, newval, out); 2203 }); 2204 } 2205 2206 void CodeGenerator::visitAtomicExchangeTypedArrayElement64( 2207 LAtomicExchangeTypedArrayElement64* lir) { 2208 Register elements = ToRegister(lir->elements()); 2209 Register64 value = ToRegister64(lir->value()); 2210 Register64 out = ToOutRegister64(lir); 2211 2212 Scalar::Type arrayType = lir->mir()->arrayType(); 2213 2214 auto dest = ToAddressOrBaseIndex(elements, lir->index(), arrayType); 2215 2216 dest.match([&](const auto& dest) { 2217 masm.atomicExchange64(Synchronization::Full(), dest, value, out); 2218 }); 2219 } 2220 2221 void CodeGenerator::visitAtomicTypedArrayElementBinop64( 2222 LAtomicTypedArrayElementBinop64* lir) { 2223 MOZ_ASSERT(!lir->mir()->isForEffect()); 2224 2225 Register elements = ToRegister(lir->elements()); 2226 Register64 value = ToRegister64(lir->value()); 2227 Register64 temp = ToRegister64(lir->temp0()); 2228 Register64 out = ToOutRegister64(lir); 2229 2230 Scalar::Type arrayType = lir->mir()->arrayType(); 2231 AtomicOp atomicOp = lir->mir()->operation(); 2232 2233 auto dest = ToAddressOrBaseIndex(elements, lir->index(), arrayType); 2234 2235 dest.match([&](const auto& dest) { 2236 masm.atomicFetchOp64(Synchronization::Full(), atomicOp, value, dest, temp, 2237 out); 2238 }); 2239 } 2240 2241 void CodeGenerator::visitAtomicTypedArrayElementBinopForEffect64( 2242 LAtomicTypedArrayElementBinopForEffect64* lir) { 2243 MOZ_ASSERT(lir->mir()->isForEffect()); 2244 2245 Register elements = ToRegister(lir->elements()); 2246 Register64 value = ToRegister64(lir->value()); 2247 Register64 temp = ToRegister64(lir->temp0()); 2248 2249 Scalar::Type arrayType = lir->mir()->arrayType(); 2250 AtomicOp atomicOp = lir->mir()->operation(); 2251 2252 auto dest = ToAddressOrBaseIndex(elements, lir->index(), arrayType); 2253 2254 dest.match([&](const auto& dest) { 2255 masm.atomicEffectOp64(Synchronization::Full(), atomicOp, value, dest, temp); 2256 }); 2257 } 2258 2259 void CodeGenerator::visitAddI64(LAddI64* lir) { 2260 ARMRegister dest = toXRegister(lir->output()); 2261 ARMRegister lhs = toXRegister(lir->lhs()); 2262 Operand rhs = toXOperand(lir->rhs()); 2263 2264 masm.Add(dest, lhs, rhs); 2265 } 2266 2267 void CodeGenerator::visitMulI64(LMulI64* lir) { 2268 LInt64Allocation lhs = lir->lhs(); 2269 LInt64Allocation rhs = lir->rhs(); 2270 Register64 output = ToOutRegister64(lir); 2271 2272 if (IsConstant(rhs)) { 2273 int64_t constant = ToInt64(rhs); 2274 // Ad-hoc strength reduction, cf the x64 code as well as the 32-bit code 2275 // higher up in this file. Bug 1712298 will lift this code to the MIR 2276 // constant folding pass, or to lowering. 2277 // 2278 // This is for wasm integers only, so no input guards or overflow checking 2279 // are needed. 2280 switch (constant) { 2281 case -1: 2282 masm.Neg(ARMRegister(output.reg, 64), 2283 ARMRegister(ToRegister64(lhs).reg, 64)); 2284 break; 2285 case 0: 2286 masm.Mov(ARMRegister(output.reg, 64), xzr); 2287 break; 2288 case 1: 2289 if (ToRegister64(lhs) != output) { 2290 masm.move64(ToRegister64(lhs), output); 2291 } 2292 break; 2293 case 2: 2294 masm.Add(ARMRegister(output.reg, 64), 2295 ARMRegister(ToRegister64(lhs).reg, 64), 2296 ARMRegister(ToRegister64(lhs).reg, 64)); 2297 break; 2298 default: 2299 // Use shift if constant is nonnegative power of 2. 2300 if (constant > 0) { 2301 int32_t shift = mozilla::FloorLog2(constant); 2302 if (int64_t(1) << shift == constant) { 2303 masm.Lsl(ARMRegister(output.reg, 64), 2304 ARMRegister(ToRegister64(lhs).reg, 64), shift); 2305 break; 2306 } 2307 } 2308 masm.mul64(Imm64(constant), ToRegister64(lhs), output); 2309 break; 2310 } 2311 } else { 2312 masm.mul64(ToRegister64(lhs), ToRegister64(rhs), output); 2313 } 2314 } 2315 2316 void CodeGenerator::visitSubI64(LSubI64* lir) { 2317 ARMRegister dest = toXRegister(lir->output()); 2318 ARMRegister lhs = toXRegister(lir->lhs()); 2319 Operand rhs = toXOperand(lir->rhs()); 2320 2321 masm.Sub(dest, lhs, rhs); 2322 } 2323 2324 void CodeGenerator::visitBitOpI64(LBitOpI64* lir) { 2325 ARMRegister dest = toXRegister(lir->output()); 2326 ARMRegister lhs = toXRegister(lir->lhs()); 2327 Operand rhs = toXOperand(lir->rhs()); 2328 2329 switch (lir->bitop()) { 2330 case JSOp::BitOr: 2331 masm.Orr(dest, lhs, rhs); 2332 break; 2333 case JSOp::BitXor: 2334 masm.Eor(dest, lhs, rhs); 2335 break; 2336 case JSOp::BitAnd: 2337 masm.And(dest, lhs, rhs); 2338 break; 2339 default: 2340 MOZ_CRASH("unexpected binary opcode"); 2341 } 2342 } 2343 2344 void CodeGenerator::visitShiftI64(LShiftI64* lir) { 2345 ARMRegister lhs(ToRegister64(lir->lhs()).reg, 64); 2346 const LAllocation* rhsAlloc = lir->rhs(); 2347 ARMRegister dest(ToOutRegister64(lir).reg, 64); 2348 2349 if (rhsAlloc->isConstant()) { 2350 int32_t shift = int32_t(rhsAlloc->toConstant()->toInt64() & 0x3F); 2351 if (shift == 0) { 2352 masm.Mov(dest, lhs); 2353 } else { 2354 switch (lir->bitop()) { 2355 case JSOp::Lsh: 2356 masm.Lsl(dest, lhs, shift); 2357 break; 2358 case JSOp::Rsh: 2359 masm.Asr(dest, lhs, shift); 2360 break; 2361 case JSOp::Ursh: 2362 masm.Lsr(dest, lhs, shift); 2363 break; 2364 default: 2365 MOZ_CRASH("Unexpected shift op"); 2366 } 2367 } 2368 } else { 2369 ARMRegister rhs(ToRegister(rhsAlloc), 64); 2370 switch (lir->bitop()) { 2371 case JSOp::Lsh: 2372 masm.Lsl(dest, lhs, rhs); 2373 break; 2374 case JSOp::Rsh: 2375 masm.Asr(dest, lhs, rhs); 2376 break; 2377 case JSOp::Ursh: 2378 masm.Lsr(dest, lhs, rhs); 2379 break; 2380 default: 2381 MOZ_CRASH("Unexpected shift op"); 2382 } 2383 } 2384 } 2385 2386 void CodeGenerator::visitAddIntPtr(LAddIntPtr* ins) { 2387 ARMRegister lhs = toXRegister(ins->lhs()); 2388 Operand rhs = toXOperand(ins->rhs()); 2389 ARMRegister dest = toXRegister(ins->output()); 2390 2391 masm.Add(dest, lhs, rhs); 2392 } 2393 2394 void CodeGenerator::visitSubIntPtr(LSubIntPtr* ins) { 2395 ARMRegister lhs = toXRegister(ins->lhs()); 2396 Operand rhs = toXOperand(ins->rhs()); 2397 ARMRegister dest = toXRegister(ins->output()); 2398 2399 masm.Sub(dest, lhs, rhs); 2400 } 2401 2402 void CodeGenerator::visitMulIntPtr(LMulIntPtr* ins) { 2403 ARMRegister lhs = toXRegister(ins->lhs()); 2404 const LAllocation* rhs = ins->rhs(); 2405 ARMRegister dest = toXRegister(ins->output()); 2406 2407 if (rhs->isConstant()) { 2408 intptr_t constant = ToIntPtr(rhs); 2409 2410 switch (constant) { 2411 case -1: 2412 masm.Neg(dest, lhs); 2413 return; 2414 case 0: 2415 masm.Mov(dest, xzr); 2416 return; 2417 case 1: 2418 if (!dest.Is(lhs)) { 2419 masm.Mov(dest, lhs); 2420 } 2421 return; 2422 case 2: 2423 masm.Add(dest, lhs, lhs); 2424 return; 2425 } 2426 2427 // Use shift if constant is a power of 2. 2428 if (constant > 0 && mozilla::IsPowerOfTwo(uintptr_t(constant))) { 2429 uint32_t shift = mozilla::FloorLog2(constant); 2430 masm.Lsl(dest, lhs, shift); 2431 return; 2432 } 2433 2434 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 2435 vixl::Register scratch = temps.AcquireX(); 2436 2437 masm.Mov(scratch, constant); 2438 masm.Mul(dest, lhs, scratch); 2439 } else { 2440 masm.Mul(dest, lhs, toXRegister(rhs)); 2441 } 2442 } 2443 2444 // If we have a constant base ptr, try to add the offset to it, to generate 2445 // better code when the full address is known. The addition may overflow past 2446 // 32 bits because the front end does nothing special if the base is a large 2447 // constant and base+offset overflows; sidestep this by performing the addition 2448 // anyway, overflowing to 64-bit. 2449 2450 static Maybe<uint64_t> IsAbsoluteAddress(const LAllocation* ptr, 2451 const wasm::MemoryAccessDesc& access) { 2452 if (ptr->isConstantValue()) { 2453 const MConstant* c = ptr->toConstant(); 2454 uint64_t base_address = c->type() == MIRType::Int32 2455 ? uint64_t(uint32_t(c->toInt32())) 2456 : uint64_t(c->toInt64()); 2457 uint64_t offset = access.offset32(); 2458 return Some(base_address + offset); 2459 } 2460 return Nothing(); 2461 } 2462 2463 void CodeGenerator::visitWasmLoad(LWasmLoad* lir) { 2464 const MWasmLoad* mir = lir->mir(); 2465 2466 if (Maybe<uint64_t> absAddr = IsAbsoluteAddress(lir->ptr(), mir->access())) { 2467 masm.wasmLoadAbsolute(mir->access(), ToRegister(lir->memoryBase()), 2468 absAddr.value(), ToAnyRegister(lir->output()), 2469 Register64::Invalid()); 2470 return; 2471 } 2472 2473 // ptr is a GPR and is either a 32-bit value zero-extended to 64-bit, or a 2474 // true 64-bit value. 2475 masm.wasmLoad(mir->access(), ToRegister(lir->memoryBase()), 2476 ToRegister(lir->ptr()), ToAnyRegister(lir->output())); 2477 } 2478 2479 void CodeGenerator::visitWasmStore(LWasmStore* lir) { 2480 const MWasmStore* mir = lir->mir(); 2481 2482 if (Maybe<uint64_t> absAddr = IsAbsoluteAddress(lir->ptr(), mir->access())) { 2483 masm.wasmStoreAbsolute(mir->access(), ToAnyRegister(lir->value()), 2484 Register64::Invalid(), ToRegister(lir->memoryBase()), 2485 absAddr.value()); 2486 return; 2487 } 2488 2489 masm.wasmStore(mir->access(), ToAnyRegister(lir->value()), 2490 ToRegister(lir->memoryBase()), ToRegister(lir->ptr())); 2491 } 2492 2493 void CodeGenerator::visitWasmSelect(LWasmSelect* lir) { 2494 MIRType mirType = lir->mir()->type(); 2495 Register condReg = ToRegister(lir->condExpr()); 2496 2497 masm.test32(condReg, condReg); 2498 2499 switch (mirType) { 2500 case MIRType::Int32: 2501 case MIRType::WasmAnyRef: 2502 case MIRType::WasmArrayData: { 2503 Register outReg = ToRegister(lir->output()); 2504 Register trueReg = ToRegister(lir->trueExpr()); 2505 Register falseReg = ToRegister(lir->falseExpr()); 2506 2507 if (mirType == MIRType::Int32) { 2508 masm.Csel(ARMRegister(outReg, 32), ARMRegister(trueReg, 32), 2509 ARMRegister(falseReg, 32), Assembler::NonZero); 2510 } else { 2511 masm.Csel(ARMRegister(outReg, 64), ARMRegister(trueReg, 64), 2512 ARMRegister(falseReg, 64), Assembler::NonZero); 2513 } 2514 break; 2515 } 2516 2517 case MIRType::Float32: 2518 case MIRType::Double: 2519 case MIRType::Simd128: { 2520 FloatRegister outReg = ToFloatRegister(lir->output()); 2521 FloatRegister trueReg = ToFloatRegister(lir->trueExpr()); 2522 FloatRegister falseReg = ToFloatRegister(lir->falseExpr()); 2523 2524 switch (mirType) { 2525 case MIRType::Float32: 2526 masm.Fcsel(ARMFPRegister(outReg, 32), ARMFPRegister(trueReg, 32), 2527 ARMFPRegister(falseReg, 32), Assembler::NonZero); 2528 break; 2529 case MIRType::Double: 2530 masm.Fcsel(ARMFPRegister(outReg, 64), ARMFPRegister(trueReg, 64), 2531 ARMFPRegister(falseReg, 64), Assembler::NonZero); 2532 break; 2533 #ifdef ENABLE_WASM_SIMD 2534 case MIRType::Simd128: { 2535 MOZ_ASSERT(outReg == trueReg); 2536 Label done; 2537 masm.j(Assembler::NonZero, &done); 2538 masm.moveSimd128(falseReg, outReg); 2539 masm.bind(&done); 2540 break; 2541 } 2542 #endif 2543 default: 2544 MOZ_CRASH(); 2545 } 2546 break; 2547 } 2548 2549 default: { 2550 MOZ_CRASH("unhandled type in visitWasmSelect!"); 2551 } 2552 } 2553 } 2554 2555 // We expect to handle the cases: compare is {{U,}Int32, {U,}Int64}, Float32, 2556 // Double}, and select is {{U,}Int32, {U,}Int64}, Float32, Double}, 2557 // independently. 2558 void CodeGenerator::visitWasmCompareAndSelect(LWasmCompareAndSelect* ins) { 2559 MCompare::CompareType compTy = ins->compareType(); 2560 2561 // Set flag. 2562 if (compTy == MCompare::Compare_Int32 || compTy == MCompare::Compare_UInt32) { 2563 Register lhs = ToRegister(ins->leftExpr()); 2564 if (ins->rightExpr()->isConstant()) { 2565 masm.cmp32(lhs, Imm32(ins->rightExpr()->toConstant()->toInt32())); 2566 } else { 2567 masm.cmp32(lhs, ToRegister(ins->rightExpr())); 2568 } 2569 } else if (compTy == MCompare::Compare_Int64 || 2570 compTy == MCompare::Compare_UInt64) { 2571 Register lhs = ToRegister(ins->leftExpr()); 2572 if (ins->rightExpr()->isConstant()) { 2573 masm.cmpPtr(lhs, Imm64(ins->rightExpr()->toConstant()->toInt64())); 2574 } else { 2575 masm.cmpPtr(lhs, ToRegister(ins->rightExpr())); 2576 } 2577 } else if (compTy == MCompare::Compare_Float32) { 2578 masm.compareFloat(ToFloatRegister(ins->leftExpr()), 2579 ToFloatRegister(ins->rightExpr())); 2580 } else if (compTy == MCompare::Compare_Double) { 2581 masm.compareDouble(ToFloatRegister(ins->leftExpr()), 2582 ToFloatRegister(ins->rightExpr())); 2583 } else { 2584 // Ref types not supported yet; v128 is not yet observed to be worth 2585 // optimizing. 2586 MOZ_CRASH("CodeGenerator::visitWasmCompareAndSelect: unexpected type (1)"); 2587 } 2588 2589 // Act on flag. 2590 Assembler::Condition cond; 2591 if (compTy == MCompare::Compare_Float32 || 2592 compTy == MCompare::Compare_Double) { 2593 cond = Assembler::ConditionFromDoubleCondition( 2594 JSOpToDoubleCondition(ins->jsop())); 2595 } else { 2596 cond = JSOpToCondition(compTy, ins->jsop()); 2597 } 2598 MIRType insTy = ins->mir()->type(); 2599 if (insTy == MIRType::Int32 || insTy == MIRType::Int64) { 2600 Register destReg = ToRegister(ins->output()); 2601 Register trueReg = ToRegister(ins->ifTrueExpr()); 2602 Register falseReg = ToRegister(ins->ifFalseExpr()); 2603 size_t size = insTy == MIRType::Int32 ? 32 : 64; 2604 masm.Csel(ARMRegister(destReg, size), ARMRegister(trueReg, size), 2605 ARMRegister(falseReg, size), cond); 2606 } else if (insTy == MIRType::Float32 || insTy == MIRType::Double) { 2607 FloatRegister destReg = ToFloatRegister(ins->output()); 2608 FloatRegister trueReg = ToFloatRegister(ins->ifTrueExpr()); 2609 FloatRegister falseReg = ToFloatRegister(ins->ifFalseExpr()); 2610 size_t size = MIRTypeToSize(insTy) * 8; 2611 masm.Fcsel(ARMFPRegister(destReg, size), ARMFPRegister(trueReg, size), 2612 ARMFPRegister(falseReg, size), cond); 2613 } else { 2614 // See above. 2615 MOZ_CRASH("CodeGenerator::visitWasmCompareAndSelect: unexpected type (2)"); 2616 } 2617 } 2618 2619 void CodeGenerator::visitWasmLoadI64(LWasmLoadI64* lir) { 2620 const MWasmLoad* mir = lir->mir(); 2621 2622 if (Maybe<uint64_t> absAddr = IsAbsoluteAddress(lir->ptr(), mir->access())) { 2623 masm.wasmLoadAbsolute(mir->access(), ToRegister(lir->memoryBase()), 2624 absAddr.value(), AnyRegister(), ToOutRegister64(lir)); 2625 return; 2626 } 2627 2628 masm.wasmLoadI64(mir->access(), ToRegister(lir->memoryBase()), 2629 ToRegister(lir->ptr()), ToOutRegister64(lir)); 2630 } 2631 2632 void CodeGenerator::visitWasmStoreI64(LWasmStoreI64* lir) { 2633 const MWasmStore* mir = lir->mir(); 2634 2635 if (Maybe<uint64_t> absAddr = IsAbsoluteAddress(lir->ptr(), mir->access())) { 2636 masm.wasmStoreAbsolute(mir->access(), AnyRegister(), 2637 ToRegister64(lir->value()), 2638 ToRegister(lir->memoryBase()), absAddr.value()); 2639 return; 2640 } 2641 2642 masm.wasmStoreI64(mir->access(), ToRegister64(lir->value()), 2643 ToRegister(lir->memoryBase()), ToRegister(lir->ptr())); 2644 } 2645 2646 void CodeGenerator::visitWasmAddOffset(LWasmAddOffset* lir) { 2647 MWasmAddOffset* mir = lir->mir(); 2648 Register base = ToRegister(lir->base()); 2649 Register out = ToRegister(lir->output()); 2650 2651 masm.Adds(ARMRegister(out, 32), ARMRegister(base, 32), 2652 Operand(mir->offset())); 2653 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { 2654 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); 2655 }); 2656 addOutOfLineCode(ool, mir); 2657 masm.j(Assembler::CarrySet, ool->entry()); 2658 } 2659 2660 void CodeGenerator::visitWasmAddOffset64(LWasmAddOffset64* lir) { 2661 MWasmAddOffset* mir = lir->mir(); 2662 Register64 base = ToRegister64(lir->base()); 2663 Register64 out = ToOutRegister64(lir); 2664 2665 masm.Adds(ARMRegister(out.reg, 64), ARMRegister(base.reg, 64), 2666 Operand(mir->offset())); 2667 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { 2668 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); 2669 }); 2670 addOutOfLineCode(ool, mir); 2671 masm.j(Assembler::CarrySet, ool->entry()); 2672 } 2673 2674 void CodeGenerator::visitWasmSelectI64(LWasmSelectI64* lir) { 2675 MOZ_ASSERT(lir->mir()->type() == MIRType::Int64); 2676 Register condReg = ToRegister(lir->condExpr()); 2677 Register64 trueReg = ToRegister64(lir->trueExpr()); 2678 Register64 falseReg = ToRegister64(lir->falseExpr()); 2679 Register64 outReg = ToOutRegister64(lir); 2680 2681 masm.test32(condReg, condReg); 2682 masm.Csel(ARMRegister(outReg.reg, 64), ARMRegister(trueReg.reg, 64), 2683 ARMRegister(falseReg.reg, 64), Assembler::NonZero); 2684 } 2685 2686 void CodeGenerator::visitSignExtendInt64(LSignExtendInt64* ins) { 2687 Register64 input = ToRegister64(ins->input()); 2688 Register64 output = ToOutRegister64(ins); 2689 switch (ins->mir()->mode()) { 2690 case MSignExtendInt64::Byte: 2691 masm.move8To64SignExtend(input.reg, output); 2692 break; 2693 case MSignExtendInt64::Half: 2694 masm.move16To64SignExtend(input.reg, output); 2695 break; 2696 case MSignExtendInt64::Word: 2697 masm.move32To64SignExtend(input.reg, output); 2698 break; 2699 } 2700 } 2701 2702 void CodeGenerator::visitWasmStackArgI64(LWasmStackArgI64* ins) { 2703 const MWasmStackArg* mir = ins->mir(); 2704 Address dst(masm.getStackPointer(), mir->spOffset()); 2705 if (IsConstant(ins->arg())) { 2706 masm.store64(Imm64(ToInt64(ins->arg())), dst); 2707 } else { 2708 masm.store64(ToRegister64(ins->arg()), dst); 2709 } 2710 } 2711 2712 void CodeGenerator::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir) { 2713 LInt64Allocation input = lir->input(); 2714 Register output = ToRegister(lir->output()); 2715 2716 if (lir->mir()->bottomHalf()) { 2717 if (input.value().isMemory()) { 2718 masm.load32(ToAddress(input), output); 2719 } else { 2720 masm.move64To32(ToRegister64(input), output); 2721 } 2722 } else { 2723 MOZ_CRASH("Not implemented."); 2724 } 2725 } 2726 2727 void CodeGenerator::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir) { 2728 Register input = ToRegister(lir->input()); 2729 Register64 output = ToOutRegister64(lir); 2730 2731 if (lir->mir()->isUnsigned()) { 2732 masm.move32To64ZeroExtend(input, output); 2733 } else { 2734 masm.move32To64SignExtend(input, output); 2735 } 2736 } 2737 2738 void CodeGenerator::visitWasmExtendU32Index(LWasmExtendU32Index* lir) { 2739 // Generates no code on this platform because the input is assumed to have 2740 // canonical form. 2741 Register output = ToRegister(lir->output()); 2742 MOZ_ASSERT(ToRegister(lir->input()) == output); 2743 masm.debugAssertCanonicalInt32(output); 2744 } 2745 2746 void CodeGenerator::visitWasmWrapU32Index(LWasmWrapU32Index* lir) { 2747 // Generates no code on this platform because the input is assumed to have 2748 // canonical form. 2749 Register output = ToRegister(lir->output()); 2750 MOZ_ASSERT(ToRegister(lir->input()) == output); 2751 masm.debugAssertCanonicalInt32(output); 2752 } 2753 2754 void CodeGenerator::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir) { 2755 auto input = ToFloatRegister(lir->input()); 2756 auto output = ToRegister(lir->output()); 2757 2758 MWasmTruncateToInt32* mir = lir->mir(); 2759 MIRType fromType = mir->input()->type(); 2760 2761 MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32); 2762 2763 auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output); 2764 addOutOfLineCode(ool, mir); 2765 2766 Label* oolEntry = ool->entry(); 2767 if (mir->isUnsigned()) { 2768 if (fromType == MIRType::Double) { 2769 masm.wasmTruncateDoubleToUInt32(input, output, mir->isSaturating(), 2770 oolEntry); 2771 } else if (fromType == MIRType::Float32) { 2772 masm.wasmTruncateFloat32ToUInt32(input, output, mir->isSaturating(), 2773 oolEntry); 2774 } else { 2775 MOZ_CRASH("unexpected type"); 2776 } 2777 2778 masm.bind(ool->rejoin()); 2779 return; 2780 } 2781 2782 if (fromType == MIRType::Double) { 2783 masm.wasmTruncateDoubleToInt32(input, output, mir->isSaturating(), 2784 oolEntry); 2785 } else if (fromType == MIRType::Float32) { 2786 masm.wasmTruncateFloat32ToInt32(input, output, mir->isSaturating(), 2787 oolEntry); 2788 } else { 2789 MOZ_CRASH("unexpected type"); 2790 } 2791 2792 masm.bind(ool->rejoin()); 2793 } 2794 2795 void CodeGenerator::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir) { 2796 FloatRegister input = ToFloatRegister(lir->input()); 2797 Register64 output = ToOutRegister64(lir); 2798 2799 MWasmTruncateToInt64* mir = lir->mir(); 2800 MIRType fromType = mir->input()->type(); 2801 2802 MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32); 2803 2804 auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output); 2805 addOutOfLineCode(ool, mir); 2806 2807 Label* oolEntry = ool->entry(); 2808 Label* oolRejoin = ool->rejoin(); 2809 bool isSaturating = mir->isSaturating(); 2810 2811 if (fromType == MIRType::Double) { 2812 if (mir->isUnsigned()) { 2813 masm.wasmTruncateDoubleToUInt64(input, output, isSaturating, oolEntry, 2814 oolRejoin, InvalidFloatReg); 2815 } else { 2816 masm.wasmTruncateDoubleToInt64(input, output, isSaturating, oolEntry, 2817 oolRejoin, InvalidFloatReg); 2818 } 2819 } else { 2820 if (mir->isUnsigned()) { 2821 masm.wasmTruncateFloat32ToUInt64(input, output, isSaturating, oolEntry, 2822 oolRejoin, InvalidFloatReg); 2823 } else { 2824 masm.wasmTruncateFloat32ToInt64(input, output, isSaturating, oolEntry, 2825 oolRejoin, InvalidFloatReg); 2826 } 2827 } 2828 } 2829 2830 void CodeGeneratorARM64::visitOutOfLineWasmTruncateCheck( 2831 OutOfLineWasmTruncateCheck* ool) { 2832 FloatRegister input = ool->input(); 2833 Register output = ool->output(); 2834 Register64 output64 = ool->output64(); 2835 MIRType fromType = ool->fromType(); 2836 MIRType toType = ool->toType(); 2837 Label* oolRejoin = ool->rejoin(); 2838 TruncFlags flags = ool->flags(); 2839 const wasm::TrapSiteDesc& trapSiteDesc = ool->trapSiteDesc(); 2840 2841 if (fromType == MIRType::Float32) { 2842 if (toType == MIRType::Int32) { 2843 masm.oolWasmTruncateCheckF32ToI32(input, output, flags, trapSiteDesc, 2844 oolRejoin); 2845 } else if (toType == MIRType::Int64) { 2846 masm.oolWasmTruncateCheckF32ToI64(input, output64, flags, trapSiteDesc, 2847 oolRejoin); 2848 } else { 2849 MOZ_CRASH("unexpected type"); 2850 } 2851 } else if (fromType == MIRType::Double) { 2852 if (toType == MIRType::Int32) { 2853 masm.oolWasmTruncateCheckF64ToI32(input, output, flags, trapSiteDesc, 2854 oolRejoin); 2855 } else if (toType == MIRType::Int64) { 2856 masm.oolWasmTruncateCheckF64ToI64(input, output64, flags, trapSiteDesc, 2857 oolRejoin); 2858 } else { 2859 MOZ_CRASH("unexpected type"); 2860 } 2861 } else { 2862 MOZ_CRASH("unexpected type"); 2863 } 2864 } 2865 2866 void CodeGenerator::visitAtomicTypedArrayElementBinop( 2867 LAtomicTypedArrayElementBinop* lir) { 2868 MOZ_ASSERT(!lir->mir()->isForEffect()); 2869 2870 AnyRegister output = ToAnyRegister(lir->output()); 2871 Register elements = ToRegister(lir->elements()); 2872 Register flagTemp = ToRegister(lir->temp0()); 2873 Register outTemp = ToTempRegisterOrInvalid(lir->temp1()); 2874 Register value = ToRegister(lir->value()); 2875 2876 Scalar::Type arrayType = lir->mir()->arrayType(); 2877 2878 auto mem = ToAddressOrBaseIndex(elements, lir->index(), arrayType); 2879 2880 mem.match([&](const auto& mem) { 2881 masm.atomicFetchOpJS(arrayType, Synchronization::Full(), 2882 lir->mir()->operation(), value, mem, flagTemp, outTemp, 2883 output); 2884 }); 2885 } 2886 2887 void CodeGenerator::visitAtomicTypedArrayElementBinopForEffect( 2888 LAtomicTypedArrayElementBinopForEffect* lir) { 2889 MOZ_ASSERT(lir->mir()->isForEffect()); 2890 2891 Register elements = ToRegister(lir->elements()); 2892 Register flagTemp = ToRegister(lir->temp0()); 2893 Register value = ToRegister(lir->value()); 2894 2895 Scalar::Type arrayType = lir->mir()->arrayType(); 2896 2897 auto mem = ToAddressOrBaseIndex(elements, lir->index(), arrayType); 2898 2899 mem.match([&](const auto& mem) { 2900 masm.atomicEffectOpJS(arrayType, Synchronization::Full(), 2901 lir->mir()->operation(), value, mem, flagTemp); 2902 }); 2903 } 2904 2905 void CodeGenerator::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir) { 2906 Register64 input = ToRegister64(lir->input()); 2907 FloatRegister output = ToFloatRegister(lir->output()); 2908 2909 MIRType outputType = lir->mir()->type(); 2910 MOZ_ASSERT(outputType == MIRType::Double || outputType == MIRType::Float32); 2911 2912 if (outputType == MIRType::Double) { 2913 if (lir->mir()->isUnsigned()) { 2914 masm.convertUInt64ToDouble(input, output, Register::Invalid()); 2915 } else { 2916 masm.convertInt64ToDouble(input, output); 2917 } 2918 } else { 2919 if (lir->mir()->isUnsigned()) { 2920 masm.convertUInt64ToFloat32(input, output, Register::Invalid()); 2921 } else { 2922 masm.convertInt64ToFloat32(input, output); 2923 } 2924 } 2925 } 2926 2927 void CodeGenerator::visitDivI64(LDivI64* lir) { 2928 Register lhs = ToRegister(lir->lhs()); 2929 Register rhs = ToRegister(lir->rhs()); 2930 2931 ARMRegister lhs64 = toXRegister(lir->lhs()); 2932 ARMRegister rhs64 = toXRegister(lir->rhs()); 2933 ARMRegister output64 = toXRegister(lir->output()); 2934 2935 MDiv* mir = lir->mir(); 2936 2937 // Handle divide by zero. 2938 TrapIfDivideByZero(masm, lir, rhs64); 2939 2940 // Handle an integer overflow exception from INT64_MIN / -1. 2941 if (mir->canBeNegativeOverflow()) { 2942 Label noOverflow; 2943 masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(INT64_MIN), &noOverflow); 2944 masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), &noOverflow); 2945 masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->trapSiteDesc()); 2946 masm.bind(&noOverflow); 2947 } 2948 2949 masm.Sdiv(output64, lhs64, rhs64); 2950 } 2951 2952 void CodeGenerator::visitModI64(LModI64* lir) { 2953 ARMRegister lhs64 = toXRegister(lir->lhs()); 2954 ARMRegister rhs64 = toXRegister(lir->rhs()); 2955 ARMRegister output64 = toXRegister(lir->output()); 2956 2957 // Handle divide by zero. 2958 TrapIfDivideByZero(masm, lir, rhs64); 2959 2960 masm.Sdiv(output64, lhs64, rhs64); 2961 2962 // Compute the remainder: output = lhs - (output * rhs). 2963 masm.Msub(output64, output64, rhs64, lhs64); 2964 } 2965 2966 void CodeGenerator::visitUDivI64(LUDivI64* lir) { 2967 ARMRegister lhs64 = toXRegister(lir->lhs()); 2968 ARMRegister rhs64 = toXRegister(lir->rhs()); 2969 ARMRegister output64 = toXRegister(lir->output()); 2970 2971 // Handle divide by zero. 2972 TrapIfDivideByZero(masm, lir, rhs64); 2973 2974 masm.Udiv(output64, lhs64, rhs64); 2975 } 2976 2977 void CodeGenerator::visitUModI64(LUModI64* lir) { 2978 ARMRegister lhs64 = toXRegister(lir->lhs()); 2979 ARMRegister rhs64 = toXRegister(lir->rhs()); 2980 ARMRegister output64 = toXRegister(lir->output()); 2981 2982 // Handle divide by zero. 2983 TrapIfDivideByZero(masm, lir, rhs64); 2984 2985 masm.Udiv(output64, lhs64, rhs64); 2986 2987 // Compute the remainder: output = lhs - (output * rhs). 2988 masm.Msub(output64, output64, rhs64, lhs64); 2989 } 2990 2991 void CodeGenerator::visitSimd128(LSimd128* ins) { 2992 #ifdef ENABLE_WASM_SIMD 2993 const LDefinition* out = ins->output(); 2994 masm.loadConstantSimd128(ins->simd128(), ToFloatRegister(out)); 2995 #else 2996 MOZ_CRASH("No SIMD"); 2997 #endif 2998 } 2999 3000 void CodeGenerator::visitWasmTernarySimd128(LWasmTernarySimd128* ins) { 3001 #ifdef ENABLE_WASM_SIMD 3002 switch (ins->simdOp()) { 3003 case wasm::SimdOp::V128Bitselect: { 3004 FloatRegister lhs = ToFloatRegister(ins->v0()); 3005 FloatRegister rhs = ToFloatRegister(ins->v1()); 3006 FloatRegister controlDest = ToFloatRegister(ins->v2()); 3007 masm.bitwiseSelectSimd128(lhs, rhs, controlDest); 3008 break; 3009 } 3010 case wasm::SimdOp::F32x4RelaxedMadd: 3011 masm.fmaFloat32x4(ToFloatRegister(ins->v0()), ToFloatRegister(ins->v1()), 3012 ToFloatRegister(ins->v2())); 3013 break; 3014 case wasm::SimdOp::F32x4RelaxedNmadd: 3015 masm.fnmaFloat32x4(ToFloatRegister(ins->v0()), ToFloatRegister(ins->v1()), 3016 ToFloatRegister(ins->v2())); 3017 break; 3018 case wasm::SimdOp::F64x2RelaxedMadd: 3019 masm.fmaFloat64x2(ToFloatRegister(ins->v0()), ToFloatRegister(ins->v1()), 3020 ToFloatRegister(ins->v2())); 3021 break; 3022 case wasm::SimdOp::F64x2RelaxedNmadd: 3023 masm.fnmaFloat64x2(ToFloatRegister(ins->v0()), ToFloatRegister(ins->v1()), 3024 ToFloatRegister(ins->v2())); 3025 break; 3026 case wasm::SimdOp::I8x16RelaxedLaneSelect: 3027 case wasm::SimdOp::I16x8RelaxedLaneSelect: 3028 case wasm::SimdOp::I32x4RelaxedLaneSelect: 3029 case wasm::SimdOp::I64x2RelaxedLaneSelect: { 3030 FloatRegister lhs = ToFloatRegister(ins->v0()); 3031 FloatRegister rhs = ToFloatRegister(ins->v1()); 3032 FloatRegister maskDest = ToFloatRegister(ins->v2()); 3033 masm.laneSelectSimd128(maskDest, lhs, rhs, maskDest); 3034 break; 3035 } 3036 case wasm::SimdOp::I32x4RelaxedDotI8x16I7x16AddS: 3037 masm.dotInt8x16Int7x16ThenAdd( 3038 ToFloatRegister(ins->v0()), ToFloatRegister(ins->v1()), 3039 ToFloatRegister(ins->v2()), ToFloatRegister(ins->temp0())); 3040 break; 3041 default: 3042 MOZ_CRASH("NYI"); 3043 } 3044 #else 3045 MOZ_CRASH("No SIMD"); 3046 #endif 3047 } 3048 3049 void CodeGenerator::visitWasmBinarySimd128(LWasmBinarySimd128* ins) { 3050 #ifdef ENABLE_WASM_SIMD 3051 FloatRegister lhs = ToFloatRegister(ins->lhs()); 3052 FloatRegister rhs = ToFloatRegister(ins->rhs()); 3053 FloatRegister dest = ToFloatRegister(ins->output()); 3054 3055 switch (ins->simdOp()) { 3056 case wasm::SimdOp::V128And: 3057 masm.bitwiseAndSimd128(lhs, rhs, dest); 3058 break; 3059 case wasm::SimdOp::V128Or: 3060 masm.bitwiseOrSimd128(lhs, rhs, dest); 3061 break; 3062 case wasm::SimdOp::V128Xor: 3063 masm.bitwiseXorSimd128(lhs, rhs, dest); 3064 break; 3065 case wasm::SimdOp::V128AndNot: 3066 masm.bitwiseAndNotSimd128(lhs, rhs, dest); 3067 break; 3068 case wasm::SimdOp::I8x16AvgrU: 3069 masm.unsignedAverageInt8x16(lhs, rhs, dest); 3070 break; 3071 case wasm::SimdOp::I16x8AvgrU: 3072 masm.unsignedAverageInt16x8(lhs, rhs, dest); 3073 break; 3074 case wasm::SimdOp::I8x16Add: 3075 masm.addInt8x16(lhs, rhs, dest); 3076 break; 3077 case wasm::SimdOp::I8x16AddSatS: 3078 masm.addSatInt8x16(lhs, rhs, dest); 3079 break; 3080 case wasm::SimdOp::I8x16AddSatU: 3081 masm.unsignedAddSatInt8x16(lhs, rhs, dest); 3082 break; 3083 case wasm::SimdOp::I8x16Sub: 3084 masm.subInt8x16(lhs, rhs, dest); 3085 break; 3086 case wasm::SimdOp::I8x16SubSatS: 3087 masm.subSatInt8x16(lhs, rhs, dest); 3088 break; 3089 case wasm::SimdOp::I8x16SubSatU: 3090 masm.unsignedSubSatInt8x16(lhs, rhs, dest); 3091 break; 3092 case wasm::SimdOp::I8x16MinS: 3093 masm.minInt8x16(lhs, rhs, dest); 3094 break; 3095 case wasm::SimdOp::I8x16MinU: 3096 masm.unsignedMinInt8x16(lhs, rhs, dest); 3097 break; 3098 case wasm::SimdOp::I8x16MaxS: 3099 masm.maxInt8x16(lhs, rhs, dest); 3100 break; 3101 case wasm::SimdOp::I8x16MaxU: 3102 masm.unsignedMaxInt8x16(lhs, rhs, dest); 3103 break; 3104 case wasm::SimdOp::I16x8Add: 3105 masm.addInt16x8(lhs, rhs, dest); 3106 break; 3107 case wasm::SimdOp::I16x8AddSatS: 3108 masm.addSatInt16x8(lhs, rhs, dest); 3109 break; 3110 case wasm::SimdOp::I16x8AddSatU: 3111 masm.unsignedAddSatInt16x8(lhs, rhs, dest); 3112 break; 3113 case wasm::SimdOp::I16x8Sub: 3114 masm.subInt16x8(lhs, rhs, dest); 3115 break; 3116 case wasm::SimdOp::I16x8SubSatS: 3117 masm.subSatInt16x8(lhs, rhs, dest); 3118 break; 3119 case wasm::SimdOp::I16x8SubSatU: 3120 masm.unsignedSubSatInt16x8(lhs, rhs, dest); 3121 break; 3122 case wasm::SimdOp::I16x8Mul: 3123 masm.mulInt16x8(lhs, rhs, dest); 3124 break; 3125 case wasm::SimdOp::I16x8MinS: 3126 masm.minInt16x8(lhs, rhs, dest); 3127 break; 3128 case wasm::SimdOp::I16x8MinU: 3129 masm.unsignedMinInt16x8(lhs, rhs, dest); 3130 break; 3131 case wasm::SimdOp::I16x8MaxS: 3132 masm.maxInt16x8(lhs, rhs, dest); 3133 break; 3134 case wasm::SimdOp::I16x8MaxU: 3135 masm.unsignedMaxInt16x8(lhs, rhs, dest); 3136 break; 3137 case wasm::SimdOp::I32x4Add: 3138 masm.addInt32x4(lhs, rhs, dest); 3139 break; 3140 case wasm::SimdOp::I32x4Sub: 3141 masm.subInt32x4(lhs, rhs, dest); 3142 break; 3143 case wasm::SimdOp::I32x4Mul: 3144 masm.mulInt32x4(lhs, rhs, dest); 3145 break; 3146 case wasm::SimdOp::I32x4MinS: 3147 masm.minInt32x4(lhs, rhs, dest); 3148 break; 3149 case wasm::SimdOp::I32x4MinU: 3150 masm.unsignedMinInt32x4(lhs, rhs, dest); 3151 break; 3152 case wasm::SimdOp::I32x4MaxS: 3153 masm.maxInt32x4(lhs, rhs, dest); 3154 break; 3155 case wasm::SimdOp::I32x4MaxU: 3156 masm.unsignedMaxInt32x4(lhs, rhs, dest); 3157 break; 3158 case wasm::SimdOp::I64x2Add: 3159 masm.addInt64x2(lhs, rhs, dest); 3160 break; 3161 case wasm::SimdOp::I64x2Sub: 3162 masm.subInt64x2(lhs, rhs, dest); 3163 break; 3164 case wasm::SimdOp::I64x2Mul: { 3165 auto temp1 = ToFloatRegister(ins->temp0()); 3166 auto temp2 = ToFloatRegister(ins->temp1()); 3167 masm.mulInt64x2(lhs, rhs, dest, temp1, temp2); 3168 break; 3169 } 3170 case wasm::SimdOp::F32x4Add: 3171 masm.addFloat32x4(lhs, rhs, dest); 3172 break; 3173 case wasm::SimdOp::F32x4Sub: 3174 masm.subFloat32x4(lhs, rhs, dest); 3175 break; 3176 case wasm::SimdOp::F32x4Mul: 3177 masm.mulFloat32x4(lhs, rhs, dest); 3178 break; 3179 case wasm::SimdOp::F32x4Div: 3180 masm.divFloat32x4(lhs, rhs, dest); 3181 break; 3182 case wasm::SimdOp::F32x4Min: 3183 masm.minFloat32x4(lhs, rhs, dest); 3184 break; 3185 case wasm::SimdOp::F32x4Max: 3186 masm.maxFloat32x4(lhs, rhs, dest); 3187 break; 3188 case wasm::SimdOp::F64x2Add: 3189 masm.addFloat64x2(lhs, rhs, dest); 3190 break; 3191 case wasm::SimdOp::F64x2Sub: 3192 masm.subFloat64x2(lhs, rhs, dest); 3193 break; 3194 case wasm::SimdOp::F64x2Mul: 3195 masm.mulFloat64x2(lhs, rhs, dest); 3196 break; 3197 case wasm::SimdOp::F64x2Div: 3198 masm.divFloat64x2(lhs, rhs, dest); 3199 break; 3200 case wasm::SimdOp::F64x2Min: 3201 masm.minFloat64x2(lhs, rhs, dest); 3202 break; 3203 case wasm::SimdOp::F64x2Max: 3204 masm.maxFloat64x2(lhs, rhs, dest); 3205 break; 3206 case wasm::SimdOp::I8x16Swizzle: 3207 masm.swizzleInt8x16(lhs, rhs, dest); 3208 break; 3209 case wasm::SimdOp::I8x16RelaxedSwizzle: 3210 masm.swizzleInt8x16Relaxed(lhs, rhs, dest); 3211 break; 3212 case wasm::SimdOp::I8x16NarrowI16x8S: 3213 masm.narrowInt16x8(lhs, rhs, dest); 3214 break; 3215 case wasm::SimdOp::I8x16NarrowI16x8U: 3216 masm.unsignedNarrowInt16x8(lhs, rhs, dest); 3217 break; 3218 case wasm::SimdOp::I16x8NarrowI32x4S: 3219 masm.narrowInt32x4(lhs, rhs, dest); 3220 break; 3221 case wasm::SimdOp::I16x8NarrowI32x4U: 3222 masm.unsignedNarrowInt32x4(lhs, rhs, dest); 3223 break; 3224 case wasm::SimdOp::I8x16Eq: 3225 masm.compareInt8x16(Assembler::Equal, lhs, rhs, dest); 3226 break; 3227 case wasm::SimdOp::I8x16Ne: 3228 masm.compareInt8x16(Assembler::NotEqual, lhs, rhs, dest); 3229 break; 3230 case wasm::SimdOp::I8x16LtS: 3231 masm.compareInt8x16(Assembler::LessThan, lhs, rhs, dest); 3232 break; 3233 case wasm::SimdOp::I8x16GtS: 3234 masm.compareInt8x16(Assembler::GreaterThan, lhs, rhs, dest); 3235 break; 3236 case wasm::SimdOp::I8x16LeS: 3237 masm.compareInt8x16(Assembler::LessThanOrEqual, lhs, rhs, dest); 3238 break; 3239 case wasm::SimdOp::I8x16GeS: 3240 masm.compareInt8x16(Assembler::GreaterThanOrEqual, lhs, rhs, dest); 3241 break; 3242 case wasm::SimdOp::I8x16LtU: 3243 masm.compareInt8x16(Assembler::Below, lhs, rhs, dest); 3244 break; 3245 case wasm::SimdOp::I8x16GtU: 3246 masm.compareInt8x16(Assembler::Above, lhs, rhs, dest); 3247 break; 3248 case wasm::SimdOp::I8x16LeU: 3249 masm.compareInt8x16(Assembler::BelowOrEqual, lhs, rhs, dest); 3250 break; 3251 case wasm::SimdOp::I8x16GeU: 3252 masm.compareInt8x16(Assembler::AboveOrEqual, lhs, rhs, dest); 3253 break; 3254 case wasm::SimdOp::I16x8Eq: 3255 masm.compareInt16x8(Assembler::Equal, lhs, rhs, dest); 3256 break; 3257 case wasm::SimdOp::I16x8Ne: 3258 masm.compareInt16x8(Assembler::NotEqual, lhs, rhs, dest); 3259 break; 3260 case wasm::SimdOp::I16x8LtS: 3261 masm.compareInt16x8(Assembler::LessThan, lhs, rhs, dest); 3262 break; 3263 case wasm::SimdOp::I16x8GtS: 3264 masm.compareInt16x8(Assembler::GreaterThan, lhs, rhs, dest); 3265 break; 3266 case wasm::SimdOp::I16x8LeS: 3267 masm.compareInt16x8(Assembler::LessThanOrEqual, lhs, rhs, dest); 3268 break; 3269 case wasm::SimdOp::I16x8GeS: 3270 masm.compareInt16x8(Assembler::GreaterThanOrEqual, lhs, rhs, dest); 3271 break; 3272 case wasm::SimdOp::I16x8LtU: 3273 masm.compareInt16x8(Assembler::Below, lhs, rhs, dest); 3274 break; 3275 case wasm::SimdOp::I16x8GtU: 3276 masm.compareInt16x8(Assembler::Above, lhs, rhs, dest); 3277 break; 3278 case wasm::SimdOp::I16x8LeU: 3279 masm.compareInt16x8(Assembler::BelowOrEqual, lhs, rhs, dest); 3280 break; 3281 case wasm::SimdOp::I16x8GeU: 3282 masm.compareInt16x8(Assembler::AboveOrEqual, lhs, rhs, dest); 3283 break; 3284 case wasm::SimdOp::I32x4Eq: 3285 masm.compareInt32x4(Assembler::Equal, lhs, rhs, dest); 3286 break; 3287 case wasm::SimdOp::I32x4Ne: 3288 masm.compareInt32x4(Assembler::NotEqual, lhs, rhs, dest); 3289 break; 3290 case wasm::SimdOp::I32x4LtS: 3291 masm.compareInt32x4(Assembler::LessThan, lhs, rhs, dest); 3292 break; 3293 case wasm::SimdOp::I32x4GtS: 3294 masm.compareInt32x4(Assembler::GreaterThan, lhs, rhs, dest); 3295 break; 3296 case wasm::SimdOp::I32x4LeS: 3297 masm.compareInt32x4(Assembler::LessThanOrEqual, lhs, rhs, dest); 3298 break; 3299 case wasm::SimdOp::I32x4GeS: 3300 masm.compareInt32x4(Assembler::GreaterThanOrEqual, lhs, rhs, dest); 3301 break; 3302 case wasm::SimdOp::I32x4LtU: 3303 masm.compareInt32x4(Assembler::Below, lhs, rhs, dest); 3304 break; 3305 case wasm::SimdOp::I32x4GtU: 3306 masm.compareInt32x4(Assembler::Above, lhs, rhs, dest); 3307 break; 3308 case wasm::SimdOp::I32x4LeU: 3309 masm.compareInt32x4(Assembler::BelowOrEqual, lhs, rhs, dest); 3310 break; 3311 case wasm::SimdOp::I32x4GeU: 3312 masm.compareInt32x4(Assembler::AboveOrEqual, lhs, rhs, dest); 3313 break; 3314 case wasm::SimdOp::I64x2Eq: 3315 masm.compareInt64x2(Assembler::Equal, lhs, rhs, dest); 3316 break; 3317 case wasm::SimdOp::I64x2LtS: 3318 masm.compareInt64x2(Assembler::LessThan, lhs, rhs, dest); 3319 break; 3320 case wasm::SimdOp::I64x2GtS: 3321 masm.compareInt64x2(Assembler::GreaterThan, lhs, rhs, dest); 3322 break; 3323 case wasm::SimdOp::I64x2LeS: 3324 masm.compareInt64x2(Assembler::LessThanOrEqual, lhs, rhs, dest); 3325 break; 3326 case wasm::SimdOp::I64x2GeS: 3327 masm.compareInt64x2(Assembler::GreaterThanOrEqual, lhs, rhs, dest); 3328 break; 3329 case wasm::SimdOp::I64x2Ne: 3330 masm.compareInt64x2(Assembler::NotEqual, lhs, rhs, dest); 3331 break; 3332 case wasm::SimdOp::F32x4Eq: 3333 masm.compareFloat32x4(Assembler::Equal, lhs, rhs, dest); 3334 break; 3335 case wasm::SimdOp::F32x4Ne: 3336 masm.compareFloat32x4(Assembler::NotEqual, lhs, rhs, dest); 3337 break; 3338 case wasm::SimdOp::F32x4Lt: 3339 masm.compareFloat32x4(Assembler::LessThan, lhs, rhs, dest); 3340 break; 3341 case wasm::SimdOp::F32x4Gt: 3342 masm.compareFloat32x4(Assembler::GreaterThan, lhs, rhs, dest); 3343 break; 3344 case wasm::SimdOp::F32x4Le: 3345 masm.compareFloat32x4(Assembler::LessThanOrEqual, lhs, rhs, dest); 3346 break; 3347 case wasm::SimdOp::F32x4Ge: 3348 masm.compareFloat32x4(Assembler::GreaterThanOrEqual, lhs, rhs, dest); 3349 break; 3350 case wasm::SimdOp::F64x2Eq: 3351 masm.compareFloat64x2(Assembler::Equal, lhs, rhs, dest); 3352 break; 3353 case wasm::SimdOp::F64x2Ne: 3354 masm.compareFloat64x2(Assembler::NotEqual, lhs, rhs, dest); 3355 break; 3356 case wasm::SimdOp::F64x2Lt: 3357 masm.compareFloat64x2(Assembler::LessThan, lhs, rhs, dest); 3358 break; 3359 case wasm::SimdOp::F64x2Gt: 3360 masm.compareFloat64x2(Assembler::GreaterThan, lhs, rhs, dest); 3361 break; 3362 case wasm::SimdOp::F64x2Le: 3363 masm.compareFloat64x2(Assembler::LessThanOrEqual, lhs, rhs, dest); 3364 break; 3365 case wasm::SimdOp::F64x2Ge: 3366 masm.compareFloat64x2(Assembler::GreaterThanOrEqual, lhs, rhs, dest); 3367 break; 3368 case wasm::SimdOp::F32x4PMax: 3369 masm.pseudoMaxFloat32x4(lhs, rhs, dest); 3370 break; 3371 case wasm::SimdOp::F32x4PMin: 3372 masm.pseudoMinFloat32x4(lhs, rhs, dest); 3373 break; 3374 case wasm::SimdOp::F64x2PMax: 3375 masm.pseudoMaxFloat64x2(lhs, rhs, dest); 3376 break; 3377 case wasm::SimdOp::F64x2PMin: 3378 masm.pseudoMinFloat64x2(lhs, rhs, dest); 3379 break; 3380 case wasm::SimdOp::I32x4DotI16x8S: 3381 masm.widenDotInt16x8(lhs, rhs, dest); 3382 break; 3383 case wasm::SimdOp::I16x8ExtmulLowI8x16S: 3384 masm.extMulLowInt8x16(lhs, rhs, dest); 3385 break; 3386 case wasm::SimdOp::I16x8ExtmulHighI8x16S: 3387 masm.extMulHighInt8x16(lhs, rhs, dest); 3388 break; 3389 case wasm::SimdOp::I16x8ExtmulLowI8x16U: 3390 masm.unsignedExtMulLowInt8x16(lhs, rhs, dest); 3391 break; 3392 case wasm::SimdOp::I16x8ExtmulHighI8x16U: 3393 masm.unsignedExtMulHighInt8x16(lhs, rhs, dest); 3394 break; 3395 case wasm::SimdOp::I32x4ExtmulLowI16x8S: 3396 masm.extMulLowInt16x8(lhs, rhs, dest); 3397 break; 3398 case wasm::SimdOp::I32x4ExtmulHighI16x8S: 3399 masm.extMulHighInt16x8(lhs, rhs, dest); 3400 break; 3401 case wasm::SimdOp::I32x4ExtmulLowI16x8U: 3402 masm.unsignedExtMulLowInt16x8(lhs, rhs, dest); 3403 break; 3404 case wasm::SimdOp::I32x4ExtmulHighI16x8U: 3405 masm.unsignedExtMulHighInt16x8(lhs, rhs, dest); 3406 break; 3407 case wasm::SimdOp::I64x2ExtmulLowI32x4S: 3408 masm.extMulLowInt32x4(lhs, rhs, dest); 3409 break; 3410 case wasm::SimdOp::I64x2ExtmulHighI32x4S: 3411 masm.extMulHighInt32x4(lhs, rhs, dest); 3412 break; 3413 case wasm::SimdOp::I64x2ExtmulLowI32x4U: 3414 masm.unsignedExtMulLowInt32x4(lhs, rhs, dest); 3415 break; 3416 case wasm::SimdOp::I64x2ExtmulHighI32x4U: 3417 masm.unsignedExtMulHighInt32x4(lhs, rhs, dest); 3418 break; 3419 case wasm::SimdOp::I16x8Q15MulrSatS: 3420 masm.q15MulrSatInt16x8(lhs, rhs, dest); 3421 break; 3422 case wasm::SimdOp::F32x4RelaxedMin: 3423 masm.minFloat32x4Relaxed(lhs, rhs, dest); 3424 break; 3425 case wasm::SimdOp::F32x4RelaxedMax: 3426 masm.maxFloat32x4Relaxed(lhs, rhs, dest); 3427 break; 3428 case wasm::SimdOp::F64x2RelaxedMin: 3429 masm.minFloat64x2Relaxed(lhs, rhs, dest); 3430 break; 3431 case wasm::SimdOp::F64x2RelaxedMax: 3432 masm.maxFloat64x2Relaxed(lhs, rhs, dest); 3433 break; 3434 case wasm::SimdOp::I16x8RelaxedQ15MulrS: 3435 masm.q15MulrInt16x8Relaxed(lhs, rhs, dest); 3436 break; 3437 case wasm::SimdOp::I16x8RelaxedDotI8x16I7x16S: 3438 masm.dotInt8x16Int7x16(lhs, rhs, dest); 3439 break; 3440 default: 3441 MOZ_CRASH("Binary SimdOp not implemented"); 3442 } 3443 #else 3444 MOZ_CRASH("No SIMD"); 3445 #endif 3446 } 3447 3448 void CodeGenerator::visitWasmBinarySimd128WithConstant( 3449 LWasmBinarySimd128WithConstant* ins) { 3450 MOZ_CRASH("No SIMD"); 3451 } 3452 3453 void CodeGenerator::visitWasmVariableShiftSimd128( 3454 LWasmVariableShiftSimd128* ins) { 3455 #ifdef ENABLE_WASM_SIMD 3456 FloatRegister lhs = ToFloatRegister(ins->lhs()); 3457 Register rhs = ToRegister(ins->rhs()); 3458 FloatRegister dest = ToFloatRegister(ins->output()); 3459 3460 switch (ins->mir()->simdOp()) { 3461 case wasm::SimdOp::I8x16Shl: 3462 masm.leftShiftInt8x16(lhs, rhs, dest); 3463 break; 3464 case wasm::SimdOp::I8x16ShrS: 3465 masm.rightShiftInt8x16(lhs, rhs, dest); 3466 break; 3467 case wasm::SimdOp::I8x16ShrU: 3468 masm.unsignedRightShiftInt8x16(lhs, rhs, dest); 3469 break; 3470 case wasm::SimdOp::I16x8Shl: 3471 masm.leftShiftInt16x8(lhs, rhs, dest); 3472 break; 3473 case wasm::SimdOp::I16x8ShrS: 3474 masm.rightShiftInt16x8(lhs, rhs, dest); 3475 break; 3476 case wasm::SimdOp::I16x8ShrU: 3477 masm.unsignedRightShiftInt16x8(lhs, rhs, dest); 3478 break; 3479 case wasm::SimdOp::I32x4Shl: 3480 masm.leftShiftInt32x4(lhs, rhs, dest); 3481 break; 3482 case wasm::SimdOp::I32x4ShrS: 3483 masm.rightShiftInt32x4(lhs, rhs, dest); 3484 break; 3485 case wasm::SimdOp::I32x4ShrU: 3486 masm.unsignedRightShiftInt32x4(lhs, rhs, dest); 3487 break; 3488 case wasm::SimdOp::I64x2Shl: 3489 masm.leftShiftInt64x2(lhs, rhs, dest); 3490 break; 3491 case wasm::SimdOp::I64x2ShrS: 3492 masm.rightShiftInt64x2(lhs, rhs, dest); 3493 break; 3494 case wasm::SimdOp::I64x2ShrU: 3495 masm.unsignedRightShiftInt64x2(lhs, rhs, dest); 3496 break; 3497 default: 3498 MOZ_CRASH("Shift SimdOp not implemented"); 3499 } 3500 #else 3501 MOZ_CRASH("No SIMD"); 3502 #endif 3503 } 3504 3505 void CodeGenerator::visitWasmConstantShiftSimd128( 3506 LWasmConstantShiftSimd128* ins) { 3507 #ifdef ENABLE_WASM_SIMD 3508 FloatRegister src = ToFloatRegister(ins->src()); 3509 FloatRegister dest = ToFloatRegister(ins->output()); 3510 int32_t shift = ins->shift(); 3511 3512 if (shift == 0) { 3513 if (src != dest) { 3514 masm.moveSimd128(src, dest); 3515 } 3516 return; 3517 } 3518 3519 switch (ins->mir()->simdOp()) { 3520 case wasm::SimdOp::I8x16Shl: 3521 masm.leftShiftInt8x16(Imm32(shift), src, dest); 3522 break; 3523 case wasm::SimdOp::I8x16ShrS: 3524 masm.rightShiftInt8x16(Imm32(shift), src, dest); 3525 break; 3526 case wasm::SimdOp::I8x16ShrU: 3527 masm.unsignedRightShiftInt8x16(Imm32(shift), src, dest); 3528 break; 3529 case wasm::SimdOp::I16x8Shl: 3530 masm.leftShiftInt16x8(Imm32(shift), src, dest); 3531 break; 3532 case wasm::SimdOp::I16x8ShrS: 3533 masm.rightShiftInt16x8(Imm32(shift), src, dest); 3534 break; 3535 case wasm::SimdOp::I16x8ShrU: 3536 masm.unsignedRightShiftInt16x8(Imm32(shift), src, dest); 3537 break; 3538 case wasm::SimdOp::I32x4Shl: 3539 masm.leftShiftInt32x4(Imm32(shift), src, dest); 3540 break; 3541 case wasm::SimdOp::I32x4ShrS: 3542 masm.rightShiftInt32x4(Imm32(shift), src, dest); 3543 break; 3544 case wasm::SimdOp::I32x4ShrU: 3545 masm.unsignedRightShiftInt32x4(Imm32(shift), src, dest); 3546 break; 3547 case wasm::SimdOp::I64x2Shl: 3548 masm.leftShiftInt64x2(Imm32(shift), src, dest); 3549 break; 3550 case wasm::SimdOp::I64x2ShrS: 3551 masm.rightShiftInt64x2(Imm32(shift), src, dest); 3552 break; 3553 case wasm::SimdOp::I64x2ShrU: 3554 masm.unsignedRightShiftInt64x2(Imm32(shift), src, dest); 3555 break; 3556 default: 3557 MOZ_CRASH("Shift SimdOp not implemented"); 3558 } 3559 #else 3560 MOZ_CRASH("No SIMD"); 3561 #endif 3562 } 3563 3564 void CodeGenerator::visitWasmSignReplicationSimd128( 3565 LWasmSignReplicationSimd128* ins) { 3566 MOZ_CRASH("No SIMD"); 3567 } 3568 3569 void CodeGenerator::visitWasmShuffleSimd128(LWasmShuffleSimd128* ins) { 3570 #ifdef ENABLE_WASM_SIMD 3571 FloatRegister lhs = ToFloatRegister(ins->lhs()); 3572 FloatRegister rhs = ToFloatRegister(ins->rhs()); 3573 FloatRegister dest = ToFloatRegister(ins->output()); 3574 SimdConstant control = ins->control(); 3575 switch (ins->op()) { 3576 case SimdShuffleOp::BLEND_8x16: { 3577 masm.blendInt8x16(reinterpret_cast<const uint8_t*>(control.asInt8x16()), 3578 lhs, rhs, dest); 3579 break; 3580 } 3581 case SimdShuffleOp::BLEND_16x8: { 3582 masm.blendInt16x8(reinterpret_cast<const uint16_t*>(control.asInt16x8()), 3583 lhs, rhs, dest); 3584 break; 3585 } 3586 case SimdShuffleOp::CONCAT_RIGHT_SHIFT_8x16: { 3587 int8_t count = 16 - control.asInt8x16()[0]; 3588 MOZ_ASSERT(count > 0, "Should have been a MOVE operation"); 3589 masm.concatAndRightShiftSimd128(lhs, rhs, dest, count); 3590 break; 3591 } 3592 case SimdShuffleOp::INTERLEAVE_HIGH_8x16: { 3593 masm.interleaveHighInt8x16(lhs, rhs, dest); 3594 break; 3595 } 3596 case SimdShuffleOp::INTERLEAVE_HIGH_16x8: { 3597 masm.interleaveHighInt16x8(lhs, rhs, dest); 3598 break; 3599 } 3600 case SimdShuffleOp::INTERLEAVE_HIGH_32x4: { 3601 masm.interleaveHighInt32x4(lhs, rhs, dest); 3602 break; 3603 } 3604 case SimdShuffleOp::INTERLEAVE_HIGH_64x2: { 3605 masm.interleaveHighInt64x2(lhs, rhs, dest); 3606 break; 3607 } 3608 case SimdShuffleOp::INTERLEAVE_LOW_8x16: { 3609 masm.interleaveLowInt8x16(lhs, rhs, dest); 3610 break; 3611 } 3612 case SimdShuffleOp::INTERLEAVE_LOW_16x8: { 3613 masm.interleaveLowInt16x8(lhs, rhs, dest); 3614 break; 3615 } 3616 case SimdShuffleOp::INTERLEAVE_LOW_32x4: { 3617 masm.interleaveLowInt32x4(lhs, rhs, dest); 3618 break; 3619 } 3620 case SimdShuffleOp::INTERLEAVE_LOW_64x2: { 3621 masm.interleaveLowInt64x2(lhs, rhs, dest); 3622 break; 3623 } 3624 case SimdShuffleOp::SHUFFLE_BLEND_8x16: { 3625 masm.shuffleInt8x16(reinterpret_cast<const uint8_t*>(control.asInt8x16()), 3626 lhs, rhs, dest); 3627 break; 3628 } 3629 default: { 3630 MOZ_CRASH("Unsupported SIMD shuffle operation"); 3631 } 3632 } 3633 #else 3634 MOZ_CRASH("No SIMD"); 3635 #endif 3636 } 3637 3638 void CodeGenerator::visitWasmPermuteSimd128(LWasmPermuteSimd128* ins) { 3639 #ifdef ENABLE_WASM_SIMD 3640 FloatRegister src = ToFloatRegister(ins->src()); 3641 FloatRegister dest = ToFloatRegister(ins->output()); 3642 SimdConstant control = ins->control(); 3643 switch (ins->op()) { 3644 case SimdPermuteOp::BROADCAST_8x16: { 3645 const SimdConstant::I8x16& mask = control.asInt8x16(); 3646 int8_t source = mask[0]; 3647 masm.splatX16(source, src, dest); 3648 break; 3649 } 3650 case SimdPermuteOp::BROADCAST_16x8: { 3651 const SimdConstant::I16x8& mask = control.asInt16x8(); 3652 int16_t source = mask[0]; 3653 masm.splatX8(source, src, dest); 3654 break; 3655 } 3656 case SimdPermuteOp::MOVE: { 3657 masm.moveSimd128(src, dest); 3658 break; 3659 } 3660 case SimdPermuteOp::PERMUTE_8x16: { 3661 const SimdConstant::I8x16& mask = control.asInt8x16(); 3662 # ifdef DEBUG 3663 mozilla::DebugOnly<int> i; 3664 for (i = 0; i < 16 && mask[i] == i; i++) { 3665 } 3666 MOZ_ASSERT(i < 16, "Should have been a MOVE operation"); 3667 # endif 3668 masm.permuteInt8x16(reinterpret_cast<const uint8_t*>(mask), src, dest); 3669 break; 3670 } 3671 case SimdPermuteOp::PERMUTE_16x8: { 3672 const SimdConstant::I16x8& mask = control.asInt16x8(); 3673 # ifdef DEBUG 3674 mozilla::DebugOnly<int> i; 3675 for (i = 0; i < 8 && mask[i] == i; i++) { 3676 } 3677 MOZ_ASSERT(i < 8, "Should have been a MOVE operation"); 3678 # endif 3679 masm.permuteInt16x8(reinterpret_cast<const uint16_t*>(mask), src, dest); 3680 break; 3681 } 3682 case SimdPermuteOp::PERMUTE_32x4: { 3683 const SimdConstant::I32x4& mask = control.asInt32x4(); 3684 # ifdef DEBUG 3685 mozilla::DebugOnly<int> i; 3686 for (i = 0; i < 4 && mask[i] == i; i++) { 3687 } 3688 MOZ_ASSERT(i < 4, "Should have been a MOVE operation"); 3689 # endif 3690 masm.permuteInt32x4(reinterpret_cast<const uint32_t*>(mask), src, dest); 3691 break; 3692 } 3693 case SimdPermuteOp::ROTATE_RIGHT_8x16: { 3694 int8_t count = control.asInt8x16()[0]; 3695 MOZ_ASSERT(count > 0, "Should have been a MOVE operation"); 3696 masm.rotateRightSimd128(src, dest, count); 3697 break; 3698 } 3699 case SimdPermuteOp::SHIFT_LEFT_8x16: { 3700 int8_t count = control.asInt8x16()[0]; 3701 MOZ_ASSERT(count > 0, "Should have been a MOVE operation"); 3702 masm.leftShiftSimd128(Imm32(count), src, dest); 3703 break; 3704 } 3705 case SimdPermuteOp::SHIFT_RIGHT_8x16: { 3706 int8_t count = control.asInt8x16()[0]; 3707 MOZ_ASSERT(count > 0, "Should have been a MOVE operation"); 3708 masm.rightShiftSimd128(Imm32(count), src, dest); 3709 break; 3710 } 3711 case SimdPermuteOp::ZERO_EXTEND_8x16_TO_16x8: 3712 masm.zeroExtend8x16To16x8(src, dest); 3713 break; 3714 case SimdPermuteOp::ZERO_EXTEND_8x16_TO_32x4: 3715 masm.zeroExtend8x16To32x4(src, dest); 3716 break; 3717 case SimdPermuteOp::ZERO_EXTEND_8x16_TO_64x2: 3718 masm.zeroExtend8x16To64x2(src, dest); 3719 break; 3720 case SimdPermuteOp::ZERO_EXTEND_16x8_TO_32x4: 3721 masm.zeroExtend16x8To32x4(src, dest); 3722 break; 3723 case SimdPermuteOp::ZERO_EXTEND_16x8_TO_64x2: 3724 masm.zeroExtend16x8To64x2(src, dest); 3725 break; 3726 case SimdPermuteOp::ZERO_EXTEND_32x4_TO_64x2: 3727 masm.zeroExtend32x4To64x2(src, dest); 3728 break; 3729 case SimdPermuteOp::REVERSE_16x8: 3730 masm.reverseInt16x8(src, dest); 3731 break; 3732 case SimdPermuteOp::REVERSE_32x4: 3733 masm.reverseInt32x4(src, dest); 3734 break; 3735 case SimdPermuteOp::REVERSE_64x2: 3736 masm.reverseInt64x2(src, dest); 3737 break; 3738 default: { 3739 MOZ_CRASH("Unsupported SIMD permutation operation"); 3740 } 3741 } 3742 #else 3743 MOZ_CRASH("No SIMD"); 3744 #endif 3745 } 3746 3747 void CodeGenerator::visitWasmReplaceLaneSimd128(LWasmReplaceLaneSimd128* ins) { 3748 #ifdef ENABLE_WASM_SIMD 3749 MOZ_ASSERT(ToFloatRegister(ins->lhs()) == ToFloatRegister(ins->output())); 3750 FloatRegister lhsDest = ToFloatRegister(ins->lhs()); 3751 const LAllocation* rhs = ins->rhs(); 3752 uint32_t laneIndex = ins->mir()->laneIndex(); 3753 3754 switch (ins->mir()->simdOp()) { 3755 case wasm::SimdOp::I8x16ReplaceLane: 3756 masm.replaceLaneInt8x16(laneIndex, ToRegister(rhs), lhsDest); 3757 break; 3758 case wasm::SimdOp::I16x8ReplaceLane: 3759 masm.replaceLaneInt16x8(laneIndex, ToRegister(rhs), lhsDest); 3760 break; 3761 case wasm::SimdOp::I32x4ReplaceLane: 3762 masm.replaceLaneInt32x4(laneIndex, ToRegister(rhs), lhsDest); 3763 break; 3764 case wasm::SimdOp::F32x4ReplaceLane: 3765 masm.replaceLaneFloat32x4(laneIndex, ToFloatRegister(rhs), lhsDest); 3766 break; 3767 case wasm::SimdOp::F64x2ReplaceLane: 3768 masm.replaceLaneFloat64x2(laneIndex, ToFloatRegister(rhs), lhsDest); 3769 break; 3770 default: 3771 MOZ_CRASH("ReplaceLane SimdOp not implemented"); 3772 } 3773 #else 3774 MOZ_CRASH("No SIMD"); 3775 #endif 3776 } 3777 3778 void CodeGenerator::visitWasmReplaceInt64LaneSimd128( 3779 LWasmReplaceInt64LaneSimd128* ins) { 3780 #ifdef ENABLE_WASM_SIMD 3781 MOZ_RELEASE_ASSERT(ins->mir()->simdOp() == wasm::SimdOp::I64x2ReplaceLane); 3782 MOZ_ASSERT(ToFloatRegister(ins->lhs()) == ToFloatRegister(ins->output())); 3783 masm.replaceLaneInt64x2(ins->mir()->laneIndex(), ToRegister64(ins->rhs()), 3784 ToFloatRegister(ins->lhs())); 3785 #else 3786 MOZ_CRASH("No SIMD"); 3787 #endif 3788 } 3789 3790 void CodeGenerator::visitWasmScalarToSimd128(LWasmScalarToSimd128* ins) { 3791 #ifdef ENABLE_WASM_SIMD 3792 FloatRegister dest = ToFloatRegister(ins->output()); 3793 3794 switch (ins->mir()->simdOp()) { 3795 case wasm::SimdOp::I8x16Splat: 3796 masm.splatX16(ToRegister(ins->src()), dest); 3797 break; 3798 case wasm::SimdOp::I16x8Splat: 3799 masm.splatX8(ToRegister(ins->src()), dest); 3800 break; 3801 case wasm::SimdOp::I32x4Splat: 3802 masm.splatX4(ToRegister(ins->src()), dest); 3803 break; 3804 case wasm::SimdOp::F32x4Splat: 3805 masm.splatX4(ToFloatRegister(ins->src()), dest); 3806 break; 3807 case wasm::SimdOp::F64x2Splat: 3808 masm.splatX2(ToFloatRegister(ins->src()), dest); 3809 break; 3810 default: 3811 MOZ_CRASH("ScalarToSimd128 SimdOp not implemented"); 3812 } 3813 #else 3814 MOZ_CRASH("No SIMD"); 3815 #endif 3816 } 3817 3818 void CodeGenerator::visitWasmInt64ToSimd128(LWasmInt64ToSimd128* ins) { 3819 #ifdef ENABLE_WASM_SIMD 3820 Register64 src = ToRegister64(ins->src()); 3821 FloatRegister dest = ToFloatRegister(ins->output()); 3822 3823 switch (ins->mir()->simdOp()) { 3824 case wasm::SimdOp::I64x2Splat: 3825 masm.splatX2(src, dest); 3826 break; 3827 case wasm::SimdOp::V128Load8x8S: 3828 masm.moveGPR64ToDouble(src, dest); 3829 masm.widenLowInt8x16(dest, dest); 3830 break; 3831 case wasm::SimdOp::V128Load8x8U: 3832 masm.moveGPR64ToDouble(src, dest); 3833 masm.unsignedWidenLowInt8x16(dest, dest); 3834 break; 3835 case wasm::SimdOp::V128Load16x4S: 3836 masm.moveGPR64ToDouble(src, dest); 3837 masm.widenLowInt16x8(dest, dest); 3838 break; 3839 case wasm::SimdOp::V128Load16x4U: 3840 masm.moveGPR64ToDouble(src, dest); 3841 masm.unsignedWidenLowInt16x8(dest, dest); 3842 break; 3843 case wasm::SimdOp::V128Load32x2S: 3844 masm.moveGPR64ToDouble(src, dest); 3845 masm.widenLowInt32x4(dest, dest); 3846 break; 3847 case wasm::SimdOp::V128Load32x2U: 3848 masm.moveGPR64ToDouble(src, dest); 3849 masm.unsignedWidenLowInt32x4(dest, dest); 3850 break; 3851 default: 3852 MOZ_CRASH("Int64ToSimd128 SimdOp not implemented"); 3853 } 3854 #else 3855 MOZ_CRASH("No SIMD"); 3856 #endif 3857 } 3858 3859 void CodeGenerator::visitWasmUnarySimd128(LWasmUnarySimd128* ins) { 3860 #ifdef ENABLE_WASM_SIMD 3861 FloatRegister src = ToFloatRegister(ins->src()); 3862 FloatRegister dest = ToFloatRegister(ins->output()); 3863 3864 switch (ins->mir()->simdOp()) { 3865 case wasm::SimdOp::I8x16Neg: 3866 masm.negInt8x16(src, dest); 3867 break; 3868 case wasm::SimdOp::I16x8Neg: 3869 masm.negInt16x8(src, dest); 3870 break; 3871 case wasm::SimdOp::I16x8ExtendLowI8x16S: 3872 masm.widenLowInt8x16(src, dest); 3873 break; 3874 case wasm::SimdOp::I16x8ExtendHighI8x16S: 3875 masm.widenHighInt8x16(src, dest); 3876 break; 3877 case wasm::SimdOp::I16x8ExtendLowI8x16U: 3878 masm.unsignedWidenLowInt8x16(src, dest); 3879 break; 3880 case wasm::SimdOp::I16x8ExtendHighI8x16U: 3881 masm.unsignedWidenHighInt8x16(src, dest); 3882 break; 3883 case wasm::SimdOp::I32x4Neg: 3884 masm.negInt32x4(src, dest); 3885 break; 3886 case wasm::SimdOp::I32x4ExtendLowI16x8S: 3887 masm.widenLowInt16x8(src, dest); 3888 break; 3889 case wasm::SimdOp::I32x4ExtendHighI16x8S: 3890 masm.widenHighInt16x8(src, dest); 3891 break; 3892 case wasm::SimdOp::I32x4ExtendLowI16x8U: 3893 masm.unsignedWidenLowInt16x8(src, dest); 3894 break; 3895 case wasm::SimdOp::I32x4ExtendHighI16x8U: 3896 masm.unsignedWidenHighInt16x8(src, dest); 3897 break; 3898 case wasm::SimdOp::I32x4TruncSatF32x4S: 3899 masm.truncSatFloat32x4ToInt32x4(src, dest); 3900 break; 3901 case wasm::SimdOp::I32x4TruncSatF32x4U: 3902 masm.unsignedTruncSatFloat32x4ToInt32x4(src, dest); 3903 break; 3904 case wasm::SimdOp::I64x2Neg: 3905 masm.negInt64x2(src, dest); 3906 break; 3907 case wasm::SimdOp::I64x2ExtendLowI32x4S: 3908 masm.widenLowInt32x4(src, dest); 3909 break; 3910 case wasm::SimdOp::I64x2ExtendHighI32x4S: 3911 masm.widenHighInt32x4(src, dest); 3912 break; 3913 case wasm::SimdOp::I64x2ExtendLowI32x4U: 3914 masm.unsignedWidenLowInt32x4(src, dest); 3915 break; 3916 case wasm::SimdOp::I64x2ExtendHighI32x4U: 3917 masm.unsignedWidenHighInt32x4(src, dest); 3918 break; 3919 case wasm::SimdOp::F32x4Abs: 3920 masm.absFloat32x4(src, dest); 3921 break; 3922 case wasm::SimdOp::F32x4Neg: 3923 masm.negFloat32x4(src, dest); 3924 break; 3925 case wasm::SimdOp::F32x4Sqrt: 3926 masm.sqrtFloat32x4(src, dest); 3927 break; 3928 case wasm::SimdOp::F32x4ConvertI32x4S: 3929 masm.convertInt32x4ToFloat32x4(src, dest); 3930 break; 3931 case wasm::SimdOp::F32x4ConvertI32x4U: 3932 masm.unsignedConvertInt32x4ToFloat32x4(src, dest); 3933 break; 3934 case wasm::SimdOp::F64x2Abs: 3935 masm.absFloat64x2(src, dest); 3936 break; 3937 case wasm::SimdOp::F64x2Neg: 3938 masm.negFloat64x2(src, dest); 3939 break; 3940 case wasm::SimdOp::F64x2Sqrt: 3941 masm.sqrtFloat64x2(src, dest); 3942 break; 3943 case wasm::SimdOp::V128Not: 3944 masm.bitwiseNotSimd128(src, dest); 3945 break; 3946 case wasm::SimdOp::I8x16Abs: 3947 masm.absInt8x16(src, dest); 3948 break; 3949 case wasm::SimdOp::I16x8Abs: 3950 masm.absInt16x8(src, dest); 3951 break; 3952 case wasm::SimdOp::I32x4Abs: 3953 masm.absInt32x4(src, dest); 3954 break; 3955 case wasm::SimdOp::I64x2Abs: 3956 masm.absInt64x2(src, dest); 3957 break; 3958 case wasm::SimdOp::F32x4Ceil: 3959 masm.ceilFloat32x4(src, dest); 3960 break; 3961 case wasm::SimdOp::F32x4Floor: 3962 masm.floorFloat32x4(src, dest); 3963 break; 3964 case wasm::SimdOp::F32x4Trunc: 3965 masm.truncFloat32x4(src, dest); 3966 break; 3967 case wasm::SimdOp::F32x4Nearest: 3968 masm.nearestFloat32x4(src, dest); 3969 break; 3970 case wasm::SimdOp::F64x2Ceil: 3971 masm.ceilFloat64x2(src, dest); 3972 break; 3973 case wasm::SimdOp::F64x2Floor: 3974 masm.floorFloat64x2(src, dest); 3975 break; 3976 case wasm::SimdOp::F64x2Trunc: 3977 masm.truncFloat64x2(src, dest); 3978 break; 3979 case wasm::SimdOp::F64x2Nearest: 3980 masm.nearestFloat64x2(src, dest); 3981 break; 3982 case wasm::SimdOp::F32x4DemoteF64x2Zero: 3983 masm.convertFloat64x2ToFloat32x4(src, dest); 3984 break; 3985 case wasm::SimdOp::F64x2PromoteLowF32x4: 3986 masm.convertFloat32x4ToFloat64x2(src, dest); 3987 break; 3988 case wasm::SimdOp::F64x2ConvertLowI32x4S: 3989 masm.convertInt32x4ToFloat64x2(src, dest); 3990 break; 3991 case wasm::SimdOp::F64x2ConvertLowI32x4U: 3992 masm.unsignedConvertInt32x4ToFloat64x2(src, dest); 3993 break; 3994 case wasm::SimdOp::I32x4TruncSatF64x2SZero: 3995 masm.truncSatFloat64x2ToInt32x4(src, dest, ToFloatRegister(ins->temp0())); 3996 break; 3997 case wasm::SimdOp::I32x4TruncSatF64x2UZero: 3998 masm.unsignedTruncSatFloat64x2ToInt32x4(src, dest, 3999 ToFloatRegister(ins->temp0())); 4000 break; 4001 case wasm::SimdOp::I16x8ExtaddPairwiseI8x16S: 4002 masm.extAddPairwiseInt8x16(src, dest); 4003 break; 4004 case wasm::SimdOp::I16x8ExtaddPairwiseI8x16U: 4005 masm.unsignedExtAddPairwiseInt8x16(src, dest); 4006 break; 4007 case wasm::SimdOp::I32x4ExtaddPairwiseI16x8S: 4008 masm.extAddPairwiseInt16x8(src, dest); 4009 break; 4010 case wasm::SimdOp::I32x4ExtaddPairwiseI16x8U: 4011 masm.unsignedExtAddPairwiseInt16x8(src, dest); 4012 break; 4013 case wasm::SimdOp::I8x16Popcnt: 4014 masm.popcntInt8x16(src, dest); 4015 break; 4016 case wasm::SimdOp::I32x4RelaxedTruncF32x4S: 4017 masm.truncFloat32x4ToInt32x4Relaxed(src, dest); 4018 break; 4019 case wasm::SimdOp::I32x4RelaxedTruncF32x4U: 4020 masm.unsignedTruncFloat32x4ToInt32x4Relaxed(src, dest); 4021 break; 4022 case wasm::SimdOp::I32x4RelaxedTruncF64x2SZero: 4023 masm.truncFloat64x2ToInt32x4Relaxed(src, dest); 4024 break; 4025 case wasm::SimdOp::I32x4RelaxedTruncF64x2UZero: 4026 masm.unsignedTruncFloat64x2ToInt32x4Relaxed(src, dest); 4027 break; 4028 default: 4029 MOZ_CRASH("Unary SimdOp not implemented"); 4030 } 4031 #else 4032 MOZ_CRASH("No SIMD"); 4033 #endif 4034 } 4035 4036 void CodeGenerator::visitWasmReduceSimd128(LWasmReduceSimd128* ins) { 4037 #ifdef ENABLE_WASM_SIMD 4038 FloatRegister src = ToFloatRegister(ins->src()); 4039 const LDefinition* dest = ins->output(); 4040 uint32_t imm = ins->mir()->imm(); 4041 FloatRegister temp = ToTempFloatRegisterOrInvalid(ins->temp0()); 4042 4043 switch (ins->mir()->simdOp()) { 4044 case wasm::SimdOp::V128AnyTrue: 4045 masm.anyTrueSimd128(src, ToRegister(dest)); 4046 break; 4047 case wasm::SimdOp::I8x16AllTrue: 4048 masm.allTrueInt8x16(src, ToRegister(dest)); 4049 break; 4050 case wasm::SimdOp::I16x8AllTrue: 4051 masm.allTrueInt16x8(src, ToRegister(dest)); 4052 break; 4053 case wasm::SimdOp::I32x4AllTrue: 4054 masm.allTrueInt32x4(src, ToRegister(dest)); 4055 break; 4056 case wasm::SimdOp::I64x2AllTrue: 4057 masm.allTrueInt64x2(src, ToRegister(dest)); 4058 break; 4059 case wasm::SimdOp::I8x16Bitmask: 4060 masm.bitmaskInt8x16(src, ToRegister(dest), temp); 4061 break; 4062 case wasm::SimdOp::I16x8Bitmask: 4063 masm.bitmaskInt16x8(src, ToRegister(dest), temp); 4064 break; 4065 case wasm::SimdOp::I32x4Bitmask: 4066 masm.bitmaskInt32x4(src, ToRegister(dest), temp); 4067 break; 4068 case wasm::SimdOp::I64x2Bitmask: 4069 masm.bitmaskInt64x2(src, ToRegister(dest), temp); 4070 break; 4071 case wasm::SimdOp::I8x16ExtractLaneS: 4072 masm.extractLaneInt8x16(imm, src, ToRegister(dest)); 4073 break; 4074 case wasm::SimdOp::I8x16ExtractLaneU: 4075 masm.unsignedExtractLaneInt8x16(imm, src, ToRegister(dest)); 4076 break; 4077 case wasm::SimdOp::I16x8ExtractLaneS: 4078 masm.extractLaneInt16x8(imm, src, ToRegister(dest)); 4079 break; 4080 case wasm::SimdOp::I16x8ExtractLaneU: 4081 masm.unsignedExtractLaneInt16x8(imm, src, ToRegister(dest)); 4082 break; 4083 case wasm::SimdOp::I32x4ExtractLane: 4084 masm.extractLaneInt32x4(imm, src, ToRegister(dest)); 4085 break; 4086 case wasm::SimdOp::F32x4ExtractLane: 4087 masm.extractLaneFloat32x4(imm, src, ToFloatRegister(dest)); 4088 break; 4089 case wasm::SimdOp::F64x2ExtractLane: 4090 masm.extractLaneFloat64x2(imm, src, ToFloatRegister(dest)); 4091 break; 4092 default: 4093 MOZ_CRASH("Reduce SimdOp not implemented"); 4094 } 4095 #else 4096 MOZ_CRASH("No SIMD"); 4097 #endif 4098 } 4099 4100 void CodeGenerator::visitWasmReduceAndBranchSimd128( 4101 LWasmReduceAndBranchSimd128* ins) { 4102 #ifdef ENABLE_WASM_SIMD 4103 FloatRegister src = ToFloatRegister(ins->src()); 4104 4105 ScratchSimd128Scope scratch(masm); 4106 vixl::UseScratchRegisterScope temps(&masm.asVIXL()); 4107 const Register test = temps.AcquireX().asUnsized(); 4108 4109 switch (ins->simdOp()) { 4110 case wasm::SimdOp::V128AnyTrue: 4111 masm.Addp(Simd1D(scratch), Simd2D(src)); 4112 masm.Umov(ARMRegister(test, 64), Simd1D(scratch), 0); 4113 masm.branch64(Assembler::Equal, Register64(test), Imm64(0), 4114 getJumpLabelForBranch(ins->ifFalse())); 4115 jumpToBlock(ins->ifTrue()); 4116 break; 4117 case wasm::SimdOp::I8x16AllTrue: 4118 case wasm::SimdOp::I16x8AllTrue: 4119 case wasm::SimdOp::I32x4AllTrue: 4120 case wasm::SimdOp::I64x2AllTrue: { 4121 // Compare all lanes to zero. 4122 switch (ins->simdOp()) { 4123 case wasm::SimdOp::I8x16AllTrue: 4124 masm.Cmeq(Simd16B(scratch), Simd16B(src), 0); 4125 break; 4126 case wasm::SimdOp::I16x8AllTrue: 4127 masm.Cmeq(Simd8H(scratch), Simd8H(src), 0); 4128 break; 4129 case wasm::SimdOp::I32x4AllTrue: 4130 masm.Cmeq(Simd4S(scratch), Simd4S(src), 0); 4131 break; 4132 case wasm::SimdOp::I64x2AllTrue: 4133 masm.Cmeq(Simd2D(scratch), Simd2D(src), 0); 4134 break; 4135 default: 4136 MOZ_CRASH(); 4137 } 4138 masm.Addp(Simd1D(scratch), Simd2D(scratch)); 4139 masm.Umov(ARMRegister(test, 64), Simd1D(scratch), 0); 4140 masm.branch64(Assembler::NotEqual, Register64(test), Imm64(0), 4141 getJumpLabelForBranch(ins->ifFalse())); 4142 jumpToBlock(ins->ifTrue()); 4143 break; 4144 } 4145 default: 4146 MOZ_CRASH("Reduce-and-branch SimdOp not implemented"); 4147 } 4148 #else 4149 MOZ_CRASH("No SIMD"); 4150 #endif 4151 } 4152 4153 void CodeGenerator::visitWasmReduceSimd128ToInt64( 4154 LWasmReduceSimd128ToInt64* ins) { 4155 #ifdef ENABLE_WASM_SIMD 4156 FloatRegister src = ToFloatRegister(ins->src()); 4157 Register64 dest = ToOutRegister64(ins); 4158 uint32_t imm = ins->mir()->imm(); 4159 4160 switch (ins->mir()->simdOp()) { 4161 case wasm::SimdOp::I64x2ExtractLane: 4162 masm.extractLaneInt64x2(imm, src, dest); 4163 break; 4164 default: 4165 MOZ_CRASH("Reduce SimdOp not implemented"); 4166 } 4167 #else 4168 MOZ_CRASH("No SIMD"); 4169 #endif 4170 } 4171 4172 static inline wasm::MemoryAccessDesc DeriveMemoryAccessDesc( 4173 const wasm::MemoryAccessDesc& access, Scalar::Type type) { 4174 return wasm::MemoryAccessDesc(access.memoryIndex(), type, access.align(), 4175 access.offset32(), access.trapDesc(), 4176 access.isHugeMemory()); 4177 } 4178 4179 void CodeGenerator::visitWasmLoadLaneSimd128(LWasmLoadLaneSimd128* ins) { 4180 #ifdef ENABLE_WASM_SIMD 4181 // Forward loading to wasmLoad, and use replaceLane after that. 4182 const MWasmLoadLaneSimd128* mir = ins->mir(); 4183 Register memoryBase = ToRegister(ins->memoryBase()); 4184 Register temp = ToRegister(ins->temp0()); 4185 FloatRegister src = ToFloatRegister(ins->src()); 4186 FloatRegister dest = ToFloatRegister(ins->output()); 4187 // replaceLane takes an lhsDest argument. 4188 masm.moveSimd128(src, dest); 4189 switch (mir->laneSize()) { 4190 case 1: { 4191 masm.wasmLoad(DeriveMemoryAccessDesc(mir->access(), Scalar::Int8), 4192 memoryBase, ToRegister(ins->ptr()), AnyRegister(temp)); 4193 masm.replaceLaneInt8x16(mir->laneIndex(), temp, dest); 4194 break; 4195 } 4196 case 2: { 4197 masm.wasmLoad(DeriveMemoryAccessDesc(mir->access(), Scalar::Int16), 4198 memoryBase, ToRegister(ins->ptr()), AnyRegister(temp)); 4199 masm.replaceLaneInt16x8(mir->laneIndex(), temp, dest); 4200 break; 4201 } 4202 case 4: { 4203 masm.wasmLoad(DeriveMemoryAccessDesc(mir->access(), Scalar::Int32), 4204 memoryBase, ToRegister(ins->ptr()), AnyRegister(temp)); 4205 masm.replaceLaneInt32x4(mir->laneIndex(), temp, dest); 4206 break; 4207 } 4208 case 8: { 4209 masm.wasmLoadI64(DeriveMemoryAccessDesc(mir->access(), Scalar::Int64), 4210 memoryBase, ToRegister(ins->ptr()), Register64(temp)); 4211 masm.replaceLaneInt64x2(mir->laneIndex(), Register64(temp), dest); 4212 break; 4213 } 4214 default: 4215 MOZ_CRASH("Unsupported load lane size"); 4216 } 4217 #else 4218 MOZ_CRASH("No SIMD"); 4219 #endif 4220 } 4221 4222 void CodeGenerator::visitWasmStoreLaneSimd128(LWasmStoreLaneSimd128* ins) { 4223 #ifdef ENABLE_WASM_SIMD 4224 // Forward storing to wasmStore for the result of extractLane. 4225 const MWasmStoreLaneSimd128* mir = ins->mir(); 4226 Register memoryBase = ToRegister(ins->memoryBase()); 4227 Register temp = ToRegister(ins->temp0()); 4228 FloatRegister src = ToFloatRegister(ins->src()); 4229 switch (mir->laneSize()) { 4230 case 1: { 4231 masm.extractLaneInt8x16(mir->laneIndex(), src, temp); 4232 masm.wasmStore(DeriveMemoryAccessDesc(mir->access(), Scalar::Int8), 4233 AnyRegister(temp), memoryBase, ToRegister(ins->ptr())); 4234 break; 4235 } 4236 case 2: { 4237 masm.extractLaneInt16x8(mir->laneIndex(), src, temp); 4238 masm.wasmStore(DeriveMemoryAccessDesc(mir->access(), Scalar::Int16), 4239 AnyRegister(temp), memoryBase, ToRegister(ins->ptr())); 4240 break; 4241 } 4242 case 4: { 4243 masm.extractLaneInt32x4(mir->laneIndex(), src, temp); 4244 masm.wasmStore(DeriveMemoryAccessDesc(mir->access(), Scalar::Int32), 4245 AnyRegister(temp), memoryBase, ToRegister(ins->ptr())); 4246 break; 4247 } 4248 case 8: { 4249 masm.extractLaneInt64x2(mir->laneIndex(), src, Register64(temp)); 4250 masm.wasmStoreI64(DeriveMemoryAccessDesc(mir->access(), Scalar::Int64), 4251 Register64(temp), memoryBase, ToRegister(ins->ptr())); 4252 break; 4253 } 4254 default: 4255 MOZ_CRASH("Unsupported store lane size"); 4256 } 4257 #else 4258 MOZ_CRASH("No SIMD"); 4259 #endif 4260 }