tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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), &notOverflow);
    391 
    392    // Handle overflow.
    393    if (mir->trapOnError()) {
    394      masm.branch32(Assembler::NotEqual, rhs, Imm32(-1), &notOverflow);
    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(&notOverflow);
    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 }