tor-browser

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

MacroAssembler-arm.cpp (235109B)


      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/arm/MacroAssembler-arm.h"
      8 
      9 #include "mozilla/Casting.h"
     10 #include "mozilla/DebugOnly.h"
     11 #include "mozilla/MathAlgorithms.h"
     12 #include "mozilla/Maybe.h"
     13 
     14 #include "jsmath.h"
     15 
     16 #include "jit/arm/Simulator-arm.h"
     17 #include "jit/AtomicOp.h"
     18 #include "jit/AtomicOperations.h"
     19 #include "jit/Bailouts.h"
     20 #include "jit/BaselineFrame.h"
     21 #include "jit/JitFrames.h"
     22 #include "jit/JitRuntime.h"
     23 #include "jit/MacroAssembler.h"
     24 #include "jit/MoveEmitter.h"
     25 #include "jit/ProcessExecutableMemory.h"
     26 #include "js/ScalarType.h"  // js::Scalar::Type
     27 #include "util/Memory.h"
     28 #include "vm/BigIntType.h"
     29 #include "vm/JitActivation.h"  // js::jit::JitActivation
     30 #include "vm/JSContext.h"
     31 #include "vm/StringType.h"
     32 #include "wasm/WasmStubs.h"
     33 
     34 #include "jit/MacroAssembler-inl.h"
     35 
     36 using namespace js;
     37 using namespace jit;
     38 
     39 using mozilla::Abs;
     40 using mozilla::BitwiseCast;
     41 using mozilla::DebugOnly;
     42 using mozilla::IsPositiveZero;
     43 using mozilla::Maybe;
     44 
     45 bool isValueDTRDCandidate(ValueOperand& val) {
     46  // In order to be used for a DTRD memory function, the two target registers
     47  // need to be a) Adjacent, with the tag larger than the payload, and b)
     48  // Aligned to a multiple of two.
     49  if ((val.typeReg().code() != (val.payloadReg().code() + 1))) {
     50    return false;
     51  }
     52  if ((val.payloadReg().code() & 1) != 0) {
     53    return false;
     54  }
     55  return true;
     56 }
     57 
     58 void MacroAssemblerARM::convertBoolToInt32(Register source, Register dest) {
     59  // Note that C++ bool is only 1 byte, so zero extend it to clear the
     60  // higher-order bits.
     61  as_and(dest, source, Imm8(0xff));
     62 }
     63 
     64 void MacroAssemblerARM::convertInt32ToDouble(Register src,
     65                                             FloatRegister dest_) {
     66  // Direct conversions aren't possible.
     67  VFPRegister dest = VFPRegister(dest_);
     68  as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat);
     69  as_vcvt(dest, dest.sintOverlay());
     70 }
     71 
     72 void MacroAssemblerARM::convertInt32ToDouble(const Address& src,
     73                                             FloatRegister dest) {
     74  ScratchDoubleScope scratch(asMasm());
     75  SecondScratchRegisterScope scratch2(asMasm());
     76  ma_vldr(src, scratch, scratch2);
     77  as_vcvt(dest, VFPRegister(scratch).sintOverlay());
     78 }
     79 
     80 void MacroAssemblerARM::convertInt32ToDouble(const BaseIndex& src,
     81                                             FloatRegister dest) {
     82  Register base = src.base;
     83  uint32_t scale = Imm32::ShiftOf(src.scale).value;
     84 
     85  ScratchRegisterScope scratch(asMasm());
     86  SecondScratchRegisterScope scratch2(asMasm());
     87 
     88  if (src.offset != 0) {
     89    ma_add(base, Imm32(src.offset), scratch, scratch2);
     90    base = scratch;
     91  }
     92  ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), scratch);
     93  convertInt32ToDouble(scratch, dest);
     94 }
     95 
     96 void MacroAssemblerARM::convertUInt32ToDouble(Register src,
     97                                              FloatRegister dest_) {
     98  // Direct conversions aren't possible.
     99  VFPRegister dest = VFPRegister(dest_);
    100  as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat);
    101  as_vcvt(dest, dest.uintOverlay());
    102 }
    103 
    104 static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
    105 
    106 void MacroAssemblerARM::convertUInt32ToFloat32(Register src,
    107                                               FloatRegister dest_) {
    108  // Direct conversions aren't possible.
    109  VFPRegister dest = VFPRegister(dest_);
    110  as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat);
    111  as_vcvt(VFPRegister(dest).singleOverlay(), dest.uintOverlay());
    112 }
    113 
    114 void MacroAssemblerARM::convertDoubleToFloat32(FloatRegister src,
    115                                               FloatRegister dest,
    116                                               Condition c) {
    117  as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src), false, c);
    118 }
    119 
    120 // Checks whether a double is representable as a 32-bit integer. If so, the
    121 // integer is written to the output register. Otherwise, a bailout is taken to
    122 // the given snapshot. This function overwrites the scratch float register.
    123 void MacroAssemblerARM::convertDoubleToInt32(FloatRegister src, Register dest,
    124                                             Label* fail,
    125                                             bool negativeZeroCheck) {
    126  // Convert the floating point value to an integer, if it did not fit, then
    127  // when we convert it *back* to a float, it will have a different value,
    128  // which we can test.
    129  ScratchDoubleScope scratchDouble(asMasm());
    130  ScratchRegisterScope scratch(asMasm());
    131 
    132  FloatRegister scratchSIntReg = scratchDouble.sintOverlay();
    133 
    134  ma_vcvt_F64_I32(src, scratchSIntReg);
    135  // Move the value into the dest register.
    136  ma_vxfer(scratchSIntReg, dest);
    137  ma_vcvt_I32_F64(scratchSIntReg, scratchDouble);
    138  ma_vcmp(src, scratchDouble);
    139  as_vmrs(pc);
    140  ma_b(fail, Assembler::VFP_NotEqualOrUnordered);
    141 
    142  if (negativeZeroCheck) {
    143    Label nonzero;
    144    as_cmp(dest, Imm8(0));
    145    ma_b(&nonzero, Assembler::NotEqual);
    146    // Test and bail for -0.0, when integer result is 0. Move the top word
    147    // of the double into the output reg, if it is non-zero, then the
    148    // original value was -0.0.
    149    as_vxfer(dest, InvalidReg, src, FloatToCore, Assembler::Always, 1);
    150    as_cmp(dest, Imm8(0));
    151    ma_b(fail, Assembler::LessThan);
    152    ma_mov(Imm32(0), dest);
    153    bind(&nonzero);
    154  }
    155 }
    156 
    157 // Checks whether a float32 is representable as a 32-bit integer. If so, the
    158 // integer is written to the output register. Otherwise, a bailout is taken to
    159 // the given snapshot. This function overwrites the scratch float register.
    160 void MacroAssemblerARM::convertFloat32ToInt32(FloatRegister src, Register dest,
    161                                              Label* fail,
    162                                              bool negativeZeroCheck) {
    163  // Converting the floating point value to an integer and then converting it
    164  // back to a float32 would not work, as float to int32 conversions are
    165  // clamping (e.g. float(INT32_MAX + 1) would get converted into INT32_MAX
    166  // and then back to float(INT32_MAX + 1)).  If this ever happens, we just
    167  // bail out.
    168  ScratchFloat32Scope scratchFloat(asMasm());
    169  ScratchRegisterScope scratch(asMasm());
    170 
    171  FloatRegister ScratchSIntReg = scratchFloat.sintOverlay();
    172  ma_vcvt_F32_I32(src, ScratchSIntReg);
    173 
    174  // Store the result
    175  ma_vxfer(ScratchSIntReg, dest);
    176 
    177  ma_vcvt_I32_F32(ScratchSIntReg, scratchFloat);
    178  ma_vcmp(src, scratchFloat);
    179  as_vmrs(pc);
    180  ma_b(fail, Assembler::VFP_NotEqualOrUnordered);
    181 
    182  // Bail out in the clamped cases.
    183  ma_cmp(dest, Imm32(0x7fffffff), scratch);
    184  ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::NotEqual);
    185  ma_b(fail, Assembler::Equal);
    186 
    187  if (negativeZeroCheck) {
    188    Label nonzero;
    189    as_cmp(dest, Imm8(0));
    190    ma_b(&nonzero, Assembler::NotEqual);
    191    // Test and bail for -0.0, when integer result is 0. Move the float into
    192    // the output reg, and if it is non-zero then the original value was
    193    // -0.0
    194    as_vxfer(dest, InvalidReg, VFPRegister(src).singleOverlay(), FloatToCore,
    195             Assembler::Always, 0);
    196    as_cmp(dest, Imm8(0));
    197    ma_b(fail, Assembler::LessThan);
    198    ma_mov(Imm32(0), dest);
    199    bind(&nonzero);
    200  }
    201 }
    202 
    203 void MacroAssemblerARM::convertFloat32ToDouble(FloatRegister src,
    204                                               FloatRegister dest) {
    205  MOZ_ASSERT(dest.isDouble());
    206  MOZ_ASSERT(src.isSingle());
    207  as_vcvt(VFPRegister(dest), VFPRegister(src).singleOverlay());
    208 }
    209 
    210 void MacroAssemblerARM::convertInt32ToFloat32(Register src,
    211                                              FloatRegister dest) {
    212  // Direct conversions aren't possible.
    213  as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat);
    214  as_vcvt(dest.singleOverlay(), dest.sintOverlay());
    215 }
    216 
    217 void MacroAssemblerARM::convertInt32ToFloat32(const Address& src,
    218                                              FloatRegister dest) {
    219  ScratchFloat32Scope scratch(asMasm());
    220  SecondScratchRegisterScope scratch2(asMasm());
    221  ma_vldr(src, scratch, scratch2);
    222  as_vcvt(dest, VFPRegister(scratch).sintOverlay());
    223 }
    224 
    225 void MacroAssemblerARM::convertFloat32ToFloat16(FloatRegister src,
    226                                                FloatRegister dest) {
    227  MOZ_ASSERT(ARMFlags::HasFPHalfPrecision());
    228  MOZ_ASSERT(src.isSingle());
    229  MOZ_ASSERT(dest.isSingle());
    230 
    231  as_vcvtb_s2h(dest, src);
    232 }
    233 
    234 void MacroAssemblerARM::convertFloat16ToFloat32(FloatRegister src,
    235                                                FloatRegister dest) {
    236  MOZ_ASSERT(ARMFlags::HasFPHalfPrecision());
    237  MOZ_ASSERT(src.isSingle());
    238  MOZ_ASSERT(dest.isSingle());
    239 
    240  as_vcvtb_h2s(dest, src);
    241 }
    242 
    243 void MacroAssemblerARM::convertInt32ToFloat16(Register src,
    244                                              FloatRegister dest) {
    245  // Convert Int32 to Float32.
    246  convertInt32ToFloat32(src, dest);
    247 
    248  // Convert Float32 to Float16.
    249  convertFloat32ToFloat16(dest, dest);
    250 }
    251 
    252 bool MacroAssemblerARM::alu_dbl(Register src1, Imm32 imm, Register dest,
    253                                ALUOp op, SBit s, Condition c) {
    254  if ((s == SetCC && !condsAreSafe(op)) || !can_dbl(op)) {
    255    return false;
    256  }
    257 
    258  ALUOp interop = getDestVariant(op);
    259  Imm8::TwoImm8mData both = Imm8::EncodeTwoImms(imm.value);
    260  if (both.fst().invalid()) {
    261    return false;
    262  }
    263 
    264  // For the most part, there is no good reason to set the condition codes for
    265  // the first instruction. We can do better things if the second instruction
    266  // doesn't have a dest, such as check for overflow by doing first operation
    267  // don't do second operation if first operation overflowed. This preserves
    268  // the overflow condition code. Unfortunately, it is horribly brittle.
    269  as_alu(dest, src1, Operand2(both.fst()), interop, LeaveCC, c);
    270  as_alu(dest, dest, Operand2(both.snd()), op, s, c);
    271  return true;
    272 }
    273 
    274 void MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest,
    275                               AutoRegisterScope& scratch, ALUOp op, SBit s,
    276                               Condition c) {
    277  // ma_mov should be used for moves.
    278  MOZ_ASSERT(op != OpMov);
    279  MOZ_ASSERT(op != OpMvn);
    280  MOZ_ASSERT(src1 != scratch);
    281 
    282  // As it turns out, if you ask for a compare-like instruction you *probably*
    283  // want it to set condition codes.
    284  MOZ_ASSERT_IF(dest == InvalidReg, s == SetCC);
    285 
    286  // The operator gives us the ability to determine how this can be used.
    287  Imm8 imm8 = Imm8(imm.value);
    288  // One instruction: If we can encode it using an imm8m, then do so.
    289  if (!imm8.invalid()) {
    290    as_alu(dest, src1, imm8, op, s, c);
    291    return;
    292  }
    293 
    294  // One instruction, negated:
    295  Imm32 negImm = imm;
    296  Register negDest;
    297  ALUOp negOp = ALUNeg(op, dest, scratch, &negImm, &negDest);
    298  Imm8 negImm8 = Imm8(negImm.value);
    299  // 'add r1, r2, -15' can be replaced with 'sub r1, r2, 15'.
    300  // The dest can be replaced (InvalidReg => scratch).
    301  // This is useful if we wish to negate tst. tst has an invalid (aka not
    302  // used) dest, but its negation bic requires a dest.
    303  if (negOp != OpInvalid && !negImm8.invalid()) {
    304    as_alu(negDest, src1, negImm8, negOp, s, c);
    305    return;
    306  }
    307 
    308  // Start by attempting to generate a two instruction form. Some things
    309  // cannot be made into two-inst forms correctly. Namely, adds dest, src,
    310  // 0xffff. Since we want the condition codes (and don't know which ones
    311  // will be checked), we need to assume that the overflow flag will be
    312  // checked and add{,s} dest, src, 0xff00; add{,s} dest, dest, 0xff is not
    313  // guaranteed to set the overflof flag the same as the (theoretical) one
    314  // instruction variant.
    315  if (alu_dbl(src1, imm, dest, op, s, c)) {
    316    return;
    317  }
    318 
    319  // And try with its negative.
    320  if (negOp != OpInvalid && alu_dbl(src1, negImm, negDest, negOp, s, c)) {
    321    return;
    322  }
    323 
    324  ma_mov(imm, scratch, c);
    325  as_alu(dest, src1, O2Reg(scratch), op, s, c);
    326 }
    327 
    328 void MacroAssemblerARM::ma_alu(Register src1, Operand op2, Register dest,
    329                               ALUOp op, SBit s, Assembler::Condition c) {
    330  MOZ_ASSERT(op2.tag() == Operand::Tag::OP2);
    331  as_alu(dest, src1, op2.toOp2(), op, s, c);
    332 }
    333 
    334 void MacroAssemblerARM::ma_alu(Register src1, Operand2 op2, Register dest,
    335                               ALUOp op, SBit s, Condition c) {
    336  as_alu(dest, src1, op2, op, s, c);
    337 }
    338 
    339 void MacroAssemblerARM::ma_nop() { as_nop(); }
    340 
    341 BufferOffset MacroAssemblerARM::ma_movPatchable(Imm32 imm_, Register dest,
    342                                                Assembler::Condition c) {
    343  int32_t imm = imm_.value;
    344  if (ARMFlags::HasMOVWT()) {
    345    AutoForbidPoolsAndNops afp(this,
    346                               /* max number of instructions in scope = */ 2);
    347    BufferOffset offset = as_movw(dest, Imm16(imm & 0xffff), c);
    348    as_movt(dest, Imm16(imm >> 16 & 0xffff), c);
    349    return offset;
    350  } else {
    351    return as_Imm32Pool(dest, imm, c);
    352  }
    353 }
    354 
    355 BufferOffset MacroAssemblerARM::ma_movPatchable(ImmPtr imm, Register dest,
    356                                                Assembler::Condition c) {
    357  return ma_movPatchable(Imm32(int32_t(imm.value)), dest, c);
    358 }
    359 
    360 /* static */
    361 template <class Iter>
    362 void MacroAssemblerARM::ma_mov_patch(Imm32 imm32, Register dest,
    363                                     Assembler::Condition c, RelocStyle rs,
    364                                     Iter iter) {
    365  // The current instruction must be an actual instruction,
    366  // not automatically-inserted boilerplate.
    367  MOZ_ASSERT(iter.cur());
    368  MOZ_ASSERT(iter.cur() == iter.maybeSkipAutomaticInstructions());
    369 
    370  int32_t imm = imm32.value;
    371  switch (rs) {
    372    case L_MOVWT:
    373      Assembler::as_movw_patch(dest, Imm16(imm & 0xffff), c, iter.cur());
    374      Assembler::as_movt_patch(dest, Imm16(imm >> 16 & 0xffff), c, iter.next());
    375      break;
    376    case L_LDR:
    377      Assembler::WritePoolEntry(iter.cur(), c, imm);
    378      break;
    379  }
    380 }
    381 
    382 template void MacroAssemblerARM::ma_mov_patch(Imm32 imm32, Register dest,
    383                                              Assembler::Condition c,
    384                                              RelocStyle rs,
    385                                              InstructionIterator iter);
    386 template void MacroAssemblerARM::ma_mov_patch(Imm32 imm32, Register dest,
    387                                              Assembler::Condition c,
    388                                              RelocStyle rs,
    389                                              BufferInstructionIterator iter);
    390 
    391 void MacroAssemblerARM::ma_mov(Register src, Register dest, SBit s,
    392                               Assembler::Condition c) {
    393  if (s == SetCC || dest != src) {
    394    as_mov(dest, O2Reg(src), s, c);
    395  }
    396 }
    397 
    398 void MacroAssemblerARM::ma_mov(Imm32 imm, Register dest,
    399                               Assembler::Condition c) {
    400  // Try mov with Imm8 operand.
    401  Imm8 imm8 = Imm8(imm.value);
    402  if (!imm8.invalid()) {
    403    as_alu(dest, InvalidReg, imm8, OpMov, LeaveCC, c);
    404    return;
    405  }
    406 
    407  // Try mvn with Imm8 operand.
    408  Imm8 negImm8 = Imm8(~imm.value);
    409  if (!negImm8.invalid()) {
    410    as_alu(dest, InvalidReg, negImm8, OpMvn, LeaveCC, c);
    411    return;
    412  }
    413 
    414  // Try movw/movt.
    415  if (ARMFlags::HasMOVWT()) {
    416    // ARMv7 supports movw/movt. movw zero-extends its 16 bit argument,
    417    // so we can set the register this way. movt leaves the bottom 16
    418    // bits in tact, so we always need a movw.
    419    as_movw(dest, Imm16(imm.value & 0xffff), c);
    420    if (uint32_t(imm.value) >> 16) {
    421      as_movt(dest, Imm16(uint32_t(imm.value) >> 16), c);
    422    }
    423    return;
    424  }
    425 
    426  // If we don't have movw/movt, we need a load.
    427  as_Imm32Pool(dest, imm.value, c);
    428 }
    429 
    430 void MacroAssemblerARM::ma_mov(ImmWord imm, Register dest,
    431                               Assembler::Condition c) {
    432  ma_mov(Imm32(imm.value), dest, c);
    433 }
    434 
    435 void MacroAssemblerARM::ma_mov(ImmGCPtr ptr, Register dest) {
    436  BufferOffset offset =
    437      ma_movPatchable(Imm32(uintptr_t(ptr.value)), dest, Always);
    438  writeDataRelocation(offset, ptr);
    439 }
    440 
    441 // Shifts (just a move with a shifting op2)
    442 void MacroAssemblerARM::ma_lsl(Imm32 shift, Register src, Register dst) {
    443  as_mov(dst, lsl(src, shift.value));
    444 }
    445 
    446 void MacroAssemblerARM::ma_lsr(Imm32 shift, Register src, Register dst) {
    447  as_mov(dst, lsr(src, shift.value));
    448 }
    449 
    450 void MacroAssemblerARM::ma_asr(Imm32 shift, Register src, Register dst) {
    451  as_mov(dst, asr(src, shift.value));
    452 }
    453 
    454 void MacroAssemblerARM::ma_ror(Imm32 shift, Register src, Register dst) {
    455  as_mov(dst, ror(src, shift.value));
    456 }
    457 
    458 void MacroAssemblerARM::ma_rol(Imm32 shift, Register src, Register dst) {
    459  as_mov(dst, rol(src, shift.value));
    460 }
    461 
    462 // Shifts (just a move with a shifting op2)
    463 void MacroAssemblerARM::ma_lsl(Register shift, Register src, Register dst) {
    464  as_mov(dst, lsl(src, shift));
    465 }
    466 
    467 void MacroAssemblerARM::ma_lsr(Register shift, Register src, Register dst) {
    468  as_mov(dst, lsr(src, shift));
    469 }
    470 
    471 void MacroAssemblerARM::ma_asr(Register shift, Register src, Register dst) {
    472  as_mov(dst, asr(src, shift));
    473 }
    474 
    475 void MacroAssemblerARM::ma_ror(Register shift, Register src, Register dst) {
    476  as_mov(dst, ror(src, shift));
    477 }
    478 
    479 void MacroAssemblerARM::ma_rol(Register shift, Register src, Register dst,
    480                               AutoRegisterScope& scratch) {
    481  as_rsb(scratch, shift, Imm8(32));
    482  as_mov(dst, ror(src, scratch));
    483 }
    484 
    485 // Move not (dest <- ~src)
    486 void MacroAssemblerARM::ma_mvn(Register src1, Register dest, SBit s,
    487                               Assembler::Condition c) {
    488  as_alu(dest, InvalidReg, O2Reg(src1), OpMvn, s, c);
    489 }
    490 
    491 // Negate (dest <- -src), src is a register, rather than a general op2.
    492 void MacroAssemblerARM::ma_neg(Register src1, Register dest, SBit s,
    493                               Assembler::Condition c) {
    494  as_rsb(dest, src1, Imm8(0), s, c);
    495 }
    496 
    497 void MacroAssemblerARM::ma_neg(Register64 src, Register64 dest) {
    498  as_rsb(dest.low, src.low, Imm8(0), SetCC);
    499  as_rsc(dest.high, src.high, Imm8(0));
    500 }
    501 
    502 // And.
    503 void MacroAssemblerARM::ma_and(Register src, Register dest, SBit s,
    504                               Assembler::Condition c) {
    505  ma_and(dest, src, dest);
    506 }
    507 
    508 void MacroAssemblerARM::ma_and(Register src1, Register src2, Register dest,
    509                               SBit s, Assembler::Condition c) {
    510  as_and(dest, src1, O2Reg(src2), s, c);
    511 }
    512 
    513 void MacroAssemblerARM::ma_and(Imm32 imm, Register dest,
    514                               AutoRegisterScope& scratch, SBit s,
    515                               Assembler::Condition c) {
    516  ma_alu(dest, imm, dest, scratch, OpAnd, s, c);
    517 }
    518 
    519 void MacroAssemblerARM::ma_and(Imm32 imm, Register src1, Register dest,
    520                               AutoRegisterScope& scratch, SBit s,
    521                               Assembler::Condition c) {
    522  ma_alu(src1, imm, dest, scratch, OpAnd, s, c);
    523 }
    524 
    525 // Bit clear (dest <- dest & ~imm) or (dest <- src1 & ~src2).
    526 void MacroAssemblerARM::ma_bic(Imm32 imm, Register dest,
    527                               AutoRegisterScope& scratch, SBit s,
    528                               Assembler::Condition c) {
    529  ma_alu(dest, imm, dest, scratch, OpBic, s, c);
    530 }
    531 
    532 // Exclusive or.
    533 void MacroAssemblerARM::ma_eor(Register src, Register dest, SBit s,
    534                               Assembler::Condition c) {
    535  ma_eor(dest, src, dest, s, c);
    536 }
    537 
    538 void MacroAssemblerARM::ma_eor(Register src1, Register src2, Register dest,
    539                               SBit s, Assembler::Condition c) {
    540  as_eor(dest, src1, O2Reg(src2), s, c);
    541 }
    542 
    543 void MacroAssemblerARM::ma_eor(Imm32 imm, Register dest,
    544                               AutoRegisterScope& scratch, SBit s,
    545                               Assembler::Condition c) {
    546  ma_alu(dest, imm, dest, scratch, OpEor, s, c);
    547 }
    548 
    549 void MacroAssemblerARM::ma_eor(Imm32 imm, Register src1, Register dest,
    550                               AutoRegisterScope& scratch, SBit s,
    551                               Assembler::Condition c) {
    552  ma_alu(src1, imm, dest, scratch, OpEor, s, c);
    553 }
    554 
    555 // Or.
    556 void MacroAssemblerARM::ma_orr(Register src, Register dest, SBit s,
    557                               Assembler::Condition c) {
    558  ma_orr(dest, src, dest, s, c);
    559 }
    560 
    561 void MacroAssemblerARM::ma_orr(Register src1, Register src2, Register dest,
    562                               SBit s, Assembler::Condition c) {
    563  as_orr(dest, src1, O2Reg(src2), s, c);
    564 }
    565 
    566 void MacroAssemblerARM::ma_orr(Imm32 imm, Register dest,
    567                               AutoRegisterScope& scratch, SBit s,
    568                               Assembler::Condition c) {
    569  ma_alu(dest, imm, dest, scratch, OpOrr, s, c);
    570 }
    571 
    572 void MacroAssemblerARM::ma_orr(Imm32 imm, Register src1, Register dest,
    573                               AutoRegisterScope& scratch, SBit s,
    574                               Assembler::Condition c) {
    575  ma_alu(src1, imm, dest, scratch, OpOrr, s, c);
    576 }
    577 
    578 // Arithmetic-based ops.
    579 // Add with carry.
    580 void MacroAssemblerARM::ma_adc(Imm32 imm, Register dest,
    581                               AutoRegisterScope& scratch, SBit s,
    582                               Condition c) {
    583  ma_alu(dest, imm, dest, scratch, OpAdc, s, c);
    584 }
    585 
    586 void MacroAssemblerARM::ma_adc(Register src, Register dest, SBit s,
    587                               Condition c) {
    588  as_alu(dest, dest, O2Reg(src), OpAdc, s, c);
    589 }
    590 
    591 void MacroAssemblerARM::ma_adc(Register src1, Register src2, Register dest,
    592                               SBit s, Condition c) {
    593  as_alu(dest, src1, O2Reg(src2), OpAdc, s, c);
    594 }
    595 
    596 void MacroAssemblerARM::ma_adc(Register src1, Imm32 op, Register dest,
    597                               AutoRegisterScope& scratch, SBit s,
    598                               Condition c) {
    599  ma_alu(src1, op, dest, scratch, OpAdc, s, c);
    600 }
    601 
    602 // Add.
    603 void MacroAssemblerARM::ma_add(Imm32 imm, Register dest,
    604                               AutoRegisterScope& scratch, SBit s,
    605                               Condition c) {
    606  ma_alu(dest, imm, dest, scratch, OpAdd, s, c);
    607 }
    608 
    609 void MacroAssemblerARM::ma_add(Register src1, Register dest, SBit s,
    610                               Condition c) {
    611  ma_alu(dest, O2Reg(src1), dest, OpAdd, s, c);
    612 }
    613 
    614 void MacroAssemblerARM::ma_add(Register src1, Register src2, Register dest,
    615                               SBit s, Condition c) {
    616  as_alu(dest, src1, O2Reg(src2), OpAdd, s, c);
    617 }
    618 
    619 void MacroAssemblerARM::ma_add(Register src1, Operand op, Register dest, SBit s,
    620                               Condition c) {
    621  ma_alu(src1, op, dest, OpAdd, s, c);
    622 }
    623 
    624 void MacroAssemblerARM::ma_add(Register src1, Imm32 op, Register dest,
    625                               AutoRegisterScope& scratch, SBit s,
    626                               Condition c) {
    627  ma_alu(src1, op, dest, scratch, OpAdd, s, c);
    628 }
    629 
    630 // Subtract with carry.
    631 void MacroAssemblerARM::ma_sbc(Imm32 imm, Register dest,
    632                               AutoRegisterScope& scratch, SBit s,
    633                               Condition c) {
    634  ma_alu(dest, imm, dest, scratch, OpSbc, s, c);
    635 }
    636 
    637 void MacroAssemblerARM::ma_sbc(Register src1, Register dest, SBit s,
    638                               Condition c) {
    639  as_alu(dest, dest, O2Reg(src1), OpSbc, s, c);
    640 }
    641 
    642 void MacroAssemblerARM::ma_sbc(Register src1, Register src2, Register dest,
    643                               SBit s, Condition c) {
    644  as_alu(dest, src1, O2Reg(src2), OpSbc, s, c);
    645 }
    646 
    647 // Subtract.
    648 void MacroAssemblerARM::ma_sub(Imm32 imm, Register dest,
    649                               AutoRegisterScope& scratch, SBit s,
    650                               Condition c) {
    651  ma_alu(dest, imm, dest, scratch, OpSub, s, c);
    652 }
    653 
    654 void MacroAssemblerARM::ma_sub(Register src1, Register dest, SBit s,
    655                               Condition c) {
    656  ma_alu(dest, Operand(src1), dest, OpSub, s, c);
    657 }
    658 
    659 void MacroAssemblerARM::ma_sub(Register src1, Register src2, Register dest,
    660                               SBit s, Condition c) {
    661  ma_alu(src1, Operand(src2), dest, OpSub, s, c);
    662 }
    663 
    664 void MacroAssemblerARM::ma_sub(Register src1, Operand op, Register dest, SBit s,
    665                               Condition c) {
    666  ma_alu(src1, op, dest, OpSub, s, c);
    667 }
    668 
    669 void MacroAssemblerARM::ma_sub(Register src1, Imm32 op, Register dest,
    670                               AutoRegisterScope& scratch, SBit s,
    671                               Condition c) {
    672  ma_alu(src1, op, dest, scratch, OpSub, s, c);
    673 }
    674 
    675 // Reverse subtract.
    676 void MacroAssemblerARM::ma_rsb(Imm32 imm, Register dest,
    677                               AutoRegisterScope& scratch, SBit s,
    678                               Condition c) {
    679  ma_alu(dest, imm, dest, scratch, OpRsb, s, c);
    680 }
    681 
    682 void MacroAssemblerARM::ma_rsb(Register src1, Register dest, SBit s,
    683                               Condition c) {
    684  as_alu(dest, src1, O2Reg(dest), OpRsb, s, c);
    685 }
    686 
    687 void MacroAssemblerARM::ma_rsb(Register src1, Register src2, Register dest,
    688                               SBit s, Condition c) {
    689  as_alu(dest, src1, O2Reg(src2), OpRsb, s, c);
    690 }
    691 
    692 void MacroAssemblerARM::ma_rsb(Register src1, Imm32 op2, Register dest,
    693                               AutoRegisterScope& scratch, SBit s,
    694                               Condition c) {
    695  ma_alu(src1, op2, dest, scratch, OpRsb, s, c);
    696 }
    697 
    698 // Reverse subtract with carry.
    699 void MacroAssemblerARM::ma_rsc(Imm32 imm, Register dest,
    700                               AutoRegisterScope& scratch, SBit s,
    701                               Condition c) {
    702  ma_alu(dest, imm, dest, scratch, OpRsc, s, c);
    703 }
    704 
    705 void MacroAssemblerARM::ma_rsc(Register src1, Register dest, SBit s,
    706                               Condition c) {
    707  as_alu(dest, dest, O2Reg(src1), OpRsc, s, c);
    708 }
    709 
    710 void MacroAssemblerARM::ma_rsc(Register src1, Register src2, Register dest,
    711                               SBit s, Condition c) {
    712  as_alu(dest, src1, O2Reg(src2), OpRsc, s, c);
    713 }
    714 
    715 // Compares/tests.
    716 // Compare negative (sets condition codes as src1 + src2 would).
    717 void MacroAssemblerARM::ma_cmn(Register src1, Imm32 imm,
    718                               AutoRegisterScope& scratch, Condition c) {
    719  ma_alu(src1, imm, InvalidReg, scratch, OpCmn, SetCC, c);
    720 }
    721 
    722 void MacroAssemblerARM::ma_cmn(Register src1, Register src2, Condition c) {
    723  as_alu(InvalidReg, src2, O2Reg(src1), OpCmn, SetCC, c);
    724 }
    725 
    726 void MacroAssemblerARM::ma_cmn(Register src1, Operand op, Condition c) {
    727  MOZ_CRASH("Feature NYI");
    728 }
    729 
    730 // Compare (src - src2).
    731 void MacroAssemblerARM::ma_cmp(Register src1, Imm32 imm,
    732                               AutoRegisterScope& scratch, Condition c) {
    733  ma_alu(src1, imm, InvalidReg, scratch, OpCmp, SetCC, c);
    734 }
    735 
    736 void MacroAssemblerARM::ma_cmp(Register src1, ImmTag tag, Condition c) {
    737  // ImmTag comparisons can always be done without use of a scratch register.
    738  Imm8 negtag = Imm8(-tag.value);
    739  MOZ_ASSERT(!negtag.invalid());
    740  as_cmn(src1, negtag, c);
    741 }
    742 
    743 void MacroAssemblerARM::ma_cmp(Register src1, ImmWord ptr,
    744                               AutoRegisterScope& scratch, Condition c) {
    745  ma_cmp(src1, Imm32(ptr.value), scratch, c);
    746 }
    747 
    748 void MacroAssemblerARM::ma_cmp(Register src1, ImmGCPtr ptr,
    749                               AutoRegisterScope& scratch, Condition c) {
    750  ma_mov(ptr, scratch);
    751  ma_cmp(src1, scratch, c);
    752 }
    753 
    754 void MacroAssemblerARM::ma_cmp(Register src1, Operand op,
    755                               AutoRegisterScope& scratch,
    756                               AutoRegisterScope& scratch2, Condition c) {
    757  switch (op.tag()) {
    758    case Operand::Tag::OP2:
    759      as_cmp(src1, op.toOp2(), c);
    760      break;
    761    case Operand::Tag::MEM:
    762      ma_ldr(op.toAddress(), scratch, scratch2);
    763      as_cmp(src1, O2Reg(scratch), c);
    764      break;
    765    default:
    766      MOZ_CRASH("trying to compare FP and integer registers");
    767  }
    768 }
    769 
    770 void MacroAssemblerARM::ma_cmp(Register src1, Register src2, Condition c) {
    771  as_cmp(src1, O2Reg(src2), c);
    772 }
    773 
    774 // Test for equality, (src1 ^ src2).
    775 void MacroAssemblerARM::ma_teq(Register src1, Imm32 imm,
    776                               AutoRegisterScope& scratch, Condition c) {
    777  ma_alu(src1, imm, InvalidReg, scratch, OpTeq, SetCC, c);
    778 }
    779 
    780 void MacroAssemblerARM::ma_teq(Register src1, Register src2, Condition c) {
    781  as_tst(src1, O2Reg(src2), c);
    782 }
    783 
    784 void MacroAssemblerARM::ma_teq(Register src1, Operand op, Condition c) {
    785  as_teq(src1, op.toOp2(), c);
    786 }
    787 
    788 // Test (src1 & src2).
    789 void MacroAssemblerARM::ma_tst(Register src1, Imm32 imm,
    790                               AutoRegisterScope& scratch, Condition c) {
    791  ma_alu(src1, imm, InvalidReg, scratch, OpTst, SetCC, c);
    792 }
    793 
    794 void MacroAssemblerARM::ma_tst(Register src1, Register src2, Condition c) {
    795  as_tst(src1, O2Reg(src2), c);
    796 }
    797 
    798 void MacroAssemblerARM::ma_tst(Register src1, Operand op, Condition c) {
    799  as_tst(src1, op.toOp2(), c);
    800 }
    801 
    802 void MacroAssemblerARM::ma_mul(Register src1, Register src2, Register dest) {
    803  as_mul(dest, src1, src2);
    804 }
    805 
    806 void MacroAssemblerARM::ma_mul(Register src1, Imm32 imm, Register dest,
    807                               AutoRegisterScope& scratch) {
    808  ma_mov(imm, scratch);
    809  as_mul(dest, src1, scratch);
    810 }
    811 
    812 Assembler::Condition MacroAssemblerARM::ma_check_mul(Register src1,
    813                                                     Register src2,
    814                                                     Register dest,
    815                                                     AutoRegisterScope& scratch,
    816                                                     Condition cond) {
    817  // TODO: this operation is illegal on armv6 and earlier
    818  // if src2 == scratch or src2 == dest.
    819  if (cond == Equal || cond == NotEqual) {
    820    as_smull(scratch, dest, src1, src2, SetCC);
    821    return cond;
    822  }
    823 
    824  if (cond == Overflow) {
    825    as_smull(scratch, dest, src1, src2);
    826    as_cmp(scratch, asr(dest, 31));
    827    return NotEqual;
    828  }
    829 
    830  MOZ_CRASH("Condition NYI");
    831 }
    832 
    833 Assembler::Condition MacroAssemblerARM::ma_check_mul(Register src1, Imm32 imm,
    834                                                     Register dest,
    835                                                     AutoRegisterScope& scratch,
    836                                                     Condition cond) {
    837  ma_mov(imm, scratch);
    838 
    839  if (cond == Equal || cond == NotEqual) {
    840    as_smull(scratch, dest, scratch, src1, SetCC);
    841    return cond;
    842  }
    843 
    844  if (cond == Overflow) {
    845    as_smull(scratch, dest, scratch, src1);
    846    as_cmp(scratch, asr(dest, 31));
    847    return NotEqual;
    848  }
    849 
    850  MOZ_CRASH("Condition NYI");
    851 }
    852 
    853 void MacroAssemblerARM::ma_umull(Register src1, Imm32 imm, Register destHigh,
    854                                 Register destLow, AutoRegisterScope& scratch) {
    855  ma_mov(imm, scratch);
    856  as_umull(destHigh, destLow, src1, scratch);
    857 }
    858 
    859 void MacroAssemblerARM::ma_umull(Register src1, Register src2,
    860                                 Register destHigh, Register destLow) {
    861  as_umull(destHigh, destLow, src1, src2);
    862 }
    863 
    864 void MacroAssemblerARM::ma_mod_mask(Register src, Register dest, Register hold,
    865                                    Register tmp, AutoRegisterScope& scratch,
    866                                    AutoRegisterScope& scratch2,
    867                                    int32_t shift) {
    868  // We wish to compute x % (1<<y) - 1 for a known constant, y.
    869  //
    870  // 1. Let b = (1<<y) and C = (1<<y)-1, then think of the 32 bit dividend as
    871  // a number in base b, namely c_0*1 + c_1*b + c_2*b^2 ... c_n*b^n
    872  //
    873  // 2. Since both addition and multiplication commute with modulus:
    874  //   x % C == (c_0 + c_1*b + ... + c_n*b^n) % C ==
    875  //    (c_0 % C) + (c_1%C) * (b % C) + (c_2 % C) * (b^2 % C)...
    876  //
    877  // 3. Since b == C + 1, b % C == 1, and b^n % C == 1 the whole thing
    878  // simplifies to: c_0 + c_1 + c_2 ... c_n % C
    879  //
    880  // Each c_n can easily be computed by a shift/bitextract, and the modulus
    881  // can be maintained by simply subtracting by C whenever the number gets
    882  // over C.
    883  int32_t mask = (1 << shift) - 1;
    884  Label head;
    885 
    886  // Register 'hold' holds -1 if the value was negative, 1 otherwise. The
    887  // scratch reg holds the remaining bits that have not been processed lr
    888  // serves as a temporary location to store extracted bits into as well as
    889  // holding the trial subtraction as a temp value dest is the accumulator
    890  // (and holds the final result)
    891  //
    892  // Move the whole value into tmp, setting the codition codes so we can muck
    893  // with them later.
    894  as_mov(tmp, O2Reg(src), SetCC);
    895  // Zero out the dest.
    896  ma_mov(Imm32(0), dest);
    897  // Set the hold appropriately.
    898  ma_mov(Imm32(1), hold);
    899  ma_mov(Imm32(-1), hold, Signed);
    900  as_rsb(tmp, tmp, Imm8(0), SetCC, Signed);
    901 
    902  // Begin the main loop.
    903  bind(&head);
    904  {
    905    // Extract the bottom bits.
    906    ma_and(Imm32(mask), tmp, scratch, scratch2);
    907    // Add those bits to the accumulator.
    908    ma_add(scratch, dest, dest);
    909    // Do a trial subtraction, this is the same operation as cmp, but we store
    910    // the dest.
    911    ma_sub(dest, Imm32(mask), scratch, scratch2, SetCC);
    912    // If (sum - C) > 0, store sum - C back into sum, thus performing a modulus.
    913    ma_mov(scratch, dest, LeaveCC, NotSigned);
    914    // Get rid of the bits that we extracted before, and set the condition
    915    // codes.
    916    as_mov(tmp, lsr(tmp, shift), SetCC);
    917    // If the shift produced zero, finish, otherwise, continue in the loop.
    918    ma_b(&head, NonZero);
    919  }
    920 
    921  // Check the hold to see if we need to negate the result. Hold can only be
    922  // 1 or -1, so this will never set the 0 flag.
    923  as_cmp(hold, Imm8(0));
    924  // If the hold was non-zero, negate the result to be in line with what JS
    925  // wants this will set the condition codes if we try to negate.
    926  as_rsb(dest, dest, Imm8(0), SetCC, Signed);
    927  // Since the Zero flag is not set by the compare, we can *only* set the Zero
    928  // flag in the rsb, so Zero is set iff we negated zero (e.g. the result of
    929  // the computation was -0.0).
    930 }
    931 
    932 void MacroAssemblerARM::ma_smod(Register num, Register div, Register dest,
    933                                AutoRegisterScope& scratch) {
    934  as_sdiv(scratch, num, div);
    935  as_mls(dest, num, scratch, div);
    936 }
    937 
    938 void MacroAssemblerARM::ma_umod(Register num, Register div, Register dest,
    939                                AutoRegisterScope& scratch) {
    940  as_udiv(scratch, num, div);
    941  as_mls(dest, num, scratch, div);
    942 }
    943 
    944 // Division
    945 void MacroAssemblerARM::ma_sdiv(Register num, Register div, Register dest,
    946                                Condition cond) {
    947  as_sdiv(dest, num, div, cond);
    948 }
    949 
    950 void MacroAssemblerARM::ma_udiv(Register num, Register div, Register dest,
    951                                Condition cond) {
    952  as_udiv(dest, num, div, cond);
    953 }
    954 
    955 // Miscellaneous instructions.
    956 void MacroAssemblerARM::ma_clz(Register src, Register dest, Condition cond) {
    957  as_clz(dest, src, cond);
    958 }
    959 
    960 void MacroAssemblerARM::ma_ctz(Register src, Register dest,
    961                               AutoRegisterScope& scratch) {
    962  // int c = __clz(a & -a);
    963  // return a ? 31 - c : c;
    964  as_rsb(scratch, src, Imm8(0), SetCC);
    965  as_and(dest, src, O2Reg(scratch), LeaveCC);
    966  as_clz(dest, dest);
    967  as_rsb(dest, dest, Imm8(0x1F), LeaveCC, Assembler::NotEqual);
    968 }
    969 
    970 // Memory.
    971 // Shortcut for when we know we're transferring 32 bits of data.
    972 void MacroAssemblerARM::ma_dtr(LoadStore ls, Register rn, Imm32 offset,
    973                               Register rt, AutoRegisterScope& scratch,
    974                               Index mode, Assembler::Condition cc) {
    975  ma_dataTransferN(ls, 32, true, rn, offset, rt, scratch, mode, cc);
    976 }
    977 
    978 FaultingCodeOffset MacroAssemblerARM::ma_dtr(LoadStore ls, Register rt,
    979                                             const Address& addr,
    980                                             AutoRegisterScope& scratch,
    981                                             Index mode, Condition cc) {
    982  BufferOffset offset = ma_dataTransferN(
    983      ls, 32, true, addr.base, Imm32(addr.offset), rt, scratch, mode, cc);
    984  return FaultingCodeOffset(offset.getOffset());
    985 }
    986 
    987 FaultingCodeOffset MacroAssemblerARM::ma_str(Register rt, DTRAddr addr,
    988                                             Index mode, Condition cc) {
    989  BufferOffset offset = as_dtr(IsStore, 32, mode, rt, addr, cc);
    990  return FaultingCodeOffset(offset.getOffset());
    991 }
    992 
    993 FaultingCodeOffset MacroAssemblerARM::ma_str(Register rt, const Address& addr,
    994                                             AutoRegisterScope& scratch,
    995                                             Index mode, Condition cc) {
    996  return ma_dtr(IsStore, rt, addr, scratch, mode, cc);
    997 }
    998 
    999 void MacroAssemblerARM::ma_strd(Register rt, DebugOnly<Register> rt2,
   1000                                EDtrAddr addr, Index mode, Condition cc) {
   1001  MOZ_ASSERT((rt.code() & 1) == 0);
   1002  MOZ_ASSERT(rt2.value.code() == rt.code() + 1);
   1003  as_extdtr(IsStore, 64, true, mode, rt, addr, cc);
   1004 }
   1005 
   1006 FaultingCodeOffset MacroAssemblerARM::ma_ldr(DTRAddr addr, Register rt,
   1007                                             Index mode, Condition cc) {
   1008  BufferOffset offset = as_dtr(IsLoad, 32, mode, rt, addr, cc);
   1009  return FaultingCodeOffset(offset.getOffset());
   1010 }
   1011 
   1012 FaultingCodeOffset MacroAssemblerARM::ma_ldr(const Address& addr, Register rt,
   1013                                             AutoRegisterScope& scratch,
   1014                                             Index mode, Condition cc) {
   1015  return ma_dtr(IsLoad, rt, addr, scratch, mode, cc);
   1016 }
   1017 
   1018 FaultingCodeOffset MacroAssemblerARM::ma_ldrb(DTRAddr addr, Register rt,
   1019                                              Index mode, Condition cc) {
   1020  BufferOffset offset = as_dtr(IsLoad, 8, mode, rt, addr, cc);
   1021  return FaultingCodeOffset(offset.getOffset());
   1022 }
   1023 
   1024 FaultingCodeOffset MacroAssemblerARM::ma_ldrsh(EDtrAddr addr, Register rt,
   1025                                               Index mode, Condition cc) {
   1026  BufferOffset offset = as_extdtr(IsLoad, 16, true, mode, rt, addr, cc);
   1027  return FaultingCodeOffset(offset.getOffset());
   1028 }
   1029 
   1030 FaultingCodeOffset MacroAssemblerARM::ma_ldrh(EDtrAddr addr, Register rt,
   1031                                              Index mode, Condition cc) {
   1032  BufferOffset offset = as_extdtr(IsLoad, 16, false, mode, rt, addr, cc);
   1033  return FaultingCodeOffset(offset.getOffset());
   1034 }
   1035 
   1036 FaultingCodeOffset MacroAssemblerARM::ma_ldrsb(EDtrAddr addr, Register rt,
   1037                                               Index mode, Condition cc) {
   1038  BufferOffset offset = as_extdtr(IsLoad, 8, true, mode, rt, addr, cc);
   1039  return FaultingCodeOffset(offset.getOffset());
   1040 }
   1041 
   1042 void MacroAssemblerARM::ma_ldrd(EDtrAddr addr, Register rt,
   1043                                DebugOnly<Register> rt2, Index mode,
   1044                                Condition cc) {
   1045  MOZ_ASSERT((rt.code() & 1) == 0);
   1046  MOZ_ASSERT(rt2.value.code() == rt.code() + 1);
   1047  MOZ_ASSERT(addr.maybeOffsetRegister() !=
   1048             rt);  // Undefined behavior if rm == rt/rt2.
   1049  MOZ_ASSERT(addr.maybeOffsetRegister() != rt2);
   1050  as_extdtr(IsLoad, 64, true, mode, rt, addr, cc);
   1051 }
   1052 
   1053 FaultingCodeOffset MacroAssemblerARM::ma_strh(Register rt, EDtrAddr addr,
   1054                                              Index mode, Condition cc) {
   1055  BufferOffset offset = as_extdtr(IsStore, 16, false, mode, rt, addr, cc);
   1056  return FaultingCodeOffset(offset.getOffset());
   1057 }
   1058 
   1059 FaultingCodeOffset MacroAssemblerARM::ma_strb(Register rt, DTRAddr addr,
   1060                                              Index mode, Condition cc) {
   1061  BufferOffset offset = as_dtr(IsStore, 8, mode, rt, addr, cc);
   1062  return FaultingCodeOffset(offset.getOffset());
   1063 }
   1064 
   1065 // Specialty for moving N bits of data, where n == 8,16,32,64.
   1066 BufferOffset MacroAssemblerARM::ma_dataTransferN(
   1067    LoadStore ls, int size, bool IsSigned, Register rn, Register rm,
   1068    Register rt, AutoRegisterScope& scratch, Index mode,
   1069    Assembler::Condition cc, Scale scale) {
   1070  MOZ_ASSERT(size == 8 || size == 16 || size == 32 || size == 64);
   1071 
   1072  if (size == 32 || (size == 8 && !IsSigned)) {
   1073    return as_dtr(ls, size, mode, rt,
   1074                  DTRAddr(rn, DtrRegImmShift(rm, LSL, scale)), cc);
   1075  }
   1076 
   1077  if (scale != TimesOne) {
   1078    ma_lsl(Imm32(scale), rm, scratch);
   1079    rm = scratch;
   1080  }
   1081 
   1082  return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(rm)),
   1083                   cc);
   1084 }
   1085 
   1086 // No scratch register is required if scale is TimesOne.
   1087 BufferOffset MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size,
   1088                                                 bool IsSigned, Register rn,
   1089                                                 Register rm, Register rt,
   1090                                                 Index mode,
   1091                                                 Assembler::Condition cc) {
   1092  MOZ_ASSERT(size == 8 || size == 16 || size == 32 || size == 64);
   1093  if (size == 32 || (size == 8 && !IsSigned)) {
   1094    return as_dtr(ls, size, mode, rt,
   1095                  DTRAddr(rn, DtrRegImmShift(rm, LSL, TimesOne)), cc);
   1096  }
   1097  return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(rm)),
   1098                   cc);
   1099 }
   1100 
   1101 BufferOffset MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size,
   1102                                                 bool IsSigned, Register rn,
   1103                                                 Imm32 offset, Register rt,
   1104                                                 AutoRegisterScope& scratch,
   1105                                                 Index mode,
   1106                                                 Assembler::Condition cc) {
   1107  MOZ_ASSERT(!(ls == IsLoad && mode == PostIndex && rt == pc),
   1108             "Large-offset PostIndex loading into PC requires special logic: "
   1109             "see ma_popn_pc().");
   1110 
   1111  int off = offset.value;
   1112 
   1113  // We can encode this as a standard ldr.
   1114  if (size == 32 || (size == 8 && !IsSigned)) {
   1115    if (off < 4096 && off > -4096) {
   1116      // This encodes as a single instruction, Emulating mode's behavior
   1117      // in a multi-instruction sequence is not necessary.
   1118      return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrOffImm(off)), cc);
   1119    }
   1120 
   1121    // We cannot encode this offset in a single ldr. For mode == index,
   1122    // try to encode it as |add scratch, base, imm; ldr dest, [scratch,
   1123    // +offset]|. This does not wark for mode == PreIndex or mode == PostIndex.
   1124    // PreIndex is simple, just do the add into the base register first,
   1125    // then do a PreIndex'ed load. PostIndexed loads can be tricky.
   1126    // Normally, doing the load with an index of 0, then doing an add would
   1127    // work, but if the destination is the PC, you don't get to execute the
   1128    // instruction after the branch, which will lead to the base register
   1129    // not being updated correctly. Explicitly handle this case, without
   1130    // doing anything fancy, then handle all of the other cases.
   1131 
   1132    // mode == Offset
   1133    //  add   scratch, base, offset_hi
   1134    //  ldr   dest, [scratch, +offset_lo]
   1135    //
   1136    // mode == PreIndex
   1137    //  add   base, base, offset_hi
   1138    //  ldr   dest, [base, +offset_lo]!
   1139 
   1140    int bottom = off & 0xfff;
   1141    int neg_bottom = 0x1000 - bottom;
   1142 
   1143    MOZ_ASSERT(rn != scratch);
   1144    MOZ_ASSERT(mode != PostIndex);
   1145 
   1146    // At this point, both off - bottom and off + neg_bottom will be
   1147    // reasonable-ish quantities.
   1148    //
   1149    // Note a neg_bottom of 0x1000 can not be encoded as an immediate
   1150    // negative offset in the instruction and this occurs when bottom is
   1151    // zero, so this case is guarded against below.
   1152    if (off < 0) {
   1153      Operand2 sub_off = Imm8(-(off - bottom));  // sub_off = bottom - off
   1154      if (!sub_off.invalid()) {
   1155        // - sub_off = off - bottom
   1156        as_sub(scratch, rn, sub_off, LeaveCC, cc);
   1157        return as_dtr(ls, size, Offset, rt, DTRAddr(scratch, DtrOffImm(bottom)),
   1158                      cc);
   1159      }
   1160 
   1161      // sub_off = -neg_bottom - off
   1162      sub_off = Imm8(-(off + neg_bottom));
   1163      if (!sub_off.invalid() && bottom != 0) {
   1164        // Guarded against by: bottom != 0
   1165        MOZ_ASSERT(neg_bottom < 0x1000);
   1166        // - sub_off = neg_bottom + off
   1167        as_sub(scratch, rn, sub_off, LeaveCC, cc);
   1168        return as_dtr(ls, size, Offset, rt,
   1169                      DTRAddr(scratch, DtrOffImm(-neg_bottom)), cc);
   1170      }
   1171    } else {
   1172      // sub_off = off - bottom
   1173      Operand2 sub_off = Imm8(off - bottom);
   1174      if (!sub_off.invalid()) {
   1175        //  sub_off = off - bottom
   1176        as_add(scratch, rn, sub_off, LeaveCC, cc);
   1177        return as_dtr(ls, size, Offset, rt, DTRAddr(scratch, DtrOffImm(bottom)),
   1178                      cc);
   1179      }
   1180 
   1181      // sub_off = neg_bottom + off
   1182      sub_off = Imm8(off + neg_bottom);
   1183      if (!sub_off.invalid() && bottom != 0) {
   1184        // Guarded against by: bottom != 0
   1185        MOZ_ASSERT(neg_bottom < 0x1000);
   1186        // sub_off = neg_bottom + off
   1187        as_add(scratch, rn, sub_off, LeaveCC, cc);
   1188        return as_dtr(ls, size, Offset, rt,
   1189                      DTRAddr(scratch, DtrOffImm(-neg_bottom)), cc);
   1190      }
   1191    }
   1192 
   1193    ma_mov(offset, scratch);
   1194    return as_dtr(ls, size, mode, rt,
   1195                  DTRAddr(rn, DtrRegImmShift(scratch, LSL, 0)));
   1196  } else {
   1197    // Should attempt to use the extended load/store instructions.
   1198    if (off < 256 && off > -256) {
   1199      return as_extdtr(ls, size, IsSigned, mode, rt,
   1200                       EDtrAddr(rn, EDtrOffImm(off)), cc);
   1201    }
   1202 
   1203    // We cannot encode this offset in a single extldr. Try to encode it as
   1204    // an add scratch, base, imm; extldr dest, [scratch, +offset].
   1205    int bottom = off & 0xff;
   1206    int neg_bottom = 0x100 - bottom;
   1207    // At this point, both off - bottom and off + neg_bottom will be
   1208    // reasonable-ish quantities.
   1209    //
   1210    // Note a neg_bottom of 0x100 can not be encoded as an immediate
   1211    // negative offset in the instruction and this occurs when bottom is
   1212    // zero, so this case is guarded against below.
   1213    if (off < 0) {
   1214      // sub_off = bottom - off
   1215      Operand2 sub_off = Imm8(-(off - bottom));
   1216      if (!sub_off.invalid()) {
   1217        // - sub_off = off - bottom
   1218        as_sub(scratch, rn, sub_off, LeaveCC, cc);
   1219        return as_extdtr(ls, size, IsSigned, Offset, rt,
   1220                         EDtrAddr(scratch, EDtrOffImm(bottom)), cc);
   1221      }
   1222      // sub_off = -neg_bottom - off
   1223      sub_off = Imm8(-(off + neg_bottom));
   1224      if (!sub_off.invalid() && bottom != 0) {
   1225        // Guarded against by: bottom != 0
   1226        MOZ_ASSERT(neg_bottom < 0x100);
   1227        // - sub_off = neg_bottom + off
   1228        as_sub(scratch, rn, sub_off, LeaveCC, cc);
   1229        return as_extdtr(ls, size, IsSigned, Offset, rt,
   1230                         EDtrAddr(scratch, EDtrOffImm(-neg_bottom)), cc);
   1231      }
   1232    } else {
   1233      // sub_off = off - bottom
   1234      Operand2 sub_off = Imm8(off - bottom);
   1235      if (!sub_off.invalid()) {
   1236        // sub_off = off - bottom
   1237        as_add(scratch, rn, sub_off, LeaveCC, cc);
   1238        return as_extdtr(ls, size, IsSigned, Offset, rt,
   1239                         EDtrAddr(scratch, EDtrOffImm(bottom)), cc);
   1240      }
   1241      // sub_off = neg_bottom + off
   1242      sub_off = Imm8(off + neg_bottom);
   1243      if (!sub_off.invalid() && bottom != 0) {
   1244        // Guarded against by: bottom != 0
   1245        MOZ_ASSERT(neg_bottom < 0x100);
   1246        // sub_off = neg_bottom + off
   1247        as_add(scratch, rn, sub_off, LeaveCC, cc);
   1248        return as_extdtr(ls, size, IsSigned, Offset, rt,
   1249                         EDtrAddr(scratch, EDtrOffImm(-neg_bottom)), cc);
   1250      }
   1251    }
   1252    ma_mov(offset, scratch);
   1253    return as_extdtr(ls, size, IsSigned, mode, rt,
   1254                     EDtrAddr(rn, EDtrOffReg(scratch)), cc);
   1255  }
   1256 }
   1257 
   1258 BufferOffset MacroAssemblerARM::ma_pop(Register r) {
   1259  return as_dtr(IsLoad, 32, PostIndex, r, DTRAddr(sp, DtrOffImm(4)));
   1260 }
   1261 
   1262 void MacroAssemblerARM::ma_popn_pc(Imm32 n, AutoRegisterScope& scratch,
   1263                                   AutoRegisterScope& scratch2) {
   1264  // pc <- [sp]; sp += n
   1265  int32_t nv = n.value;
   1266 
   1267  if (nv < 4096 && nv >= -4096) {
   1268    as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(nv)));
   1269  } else {
   1270    ma_mov(sp, scratch);
   1271    ma_add(Imm32(n), sp, scratch2);
   1272    as_dtr(IsLoad, 32, Offset, pc, DTRAddr(scratch, DtrOffImm(0)));
   1273  }
   1274 }
   1275 
   1276 void MacroAssemblerARM::ma_push(Register r) {
   1277  MOZ_ASSERT(r != sp, "Use ma_push_sp().");
   1278  as_dtr(IsStore, 32, PreIndex, r, DTRAddr(sp, DtrOffImm(-4)));
   1279 }
   1280 
   1281 void MacroAssemblerARM::ma_push_sp(Register r, AutoRegisterScope& scratch) {
   1282  // Pushing sp is not well-defined: use two instructions.
   1283  MOZ_ASSERT(r == sp);
   1284  ma_mov(sp, scratch);
   1285  as_dtr(IsStore, 32, PreIndex, scratch, DTRAddr(sp, DtrOffImm(-4)));
   1286 }
   1287 
   1288 void MacroAssemblerARM::ma_vpop(VFPRegister r) {
   1289  startFloatTransferM(IsLoad, sp, IA, WriteBack);
   1290  transferFloatReg(r);
   1291  finishFloatTransfer();
   1292 }
   1293 
   1294 void MacroAssemblerARM::ma_vpush(VFPRegister r) {
   1295  startFloatTransferM(IsStore, sp, DB, WriteBack);
   1296  transferFloatReg(r);
   1297  finishFloatTransfer();
   1298 }
   1299 
   1300 // Barriers
   1301 void MacroAssemblerARM::ma_dmb(BarrierOption option) {
   1302  if (ARMFlags::HasDMBDSBISB()) {
   1303    as_dmb(option);
   1304  } else {
   1305    as_dmb_trap();
   1306  }
   1307 }
   1308 
   1309 void MacroAssemblerARM::ma_dsb(BarrierOption option) {
   1310  if (ARMFlags::HasDMBDSBISB()) {
   1311    as_dsb(option);
   1312  } else {
   1313    as_dsb_trap();
   1314  }
   1315 }
   1316 
   1317 // Branches when done from within arm-specific code.
   1318 BufferOffset MacroAssemblerARM::ma_b(Label* dest, Assembler::Condition c) {
   1319  return as_b(dest, c);
   1320 }
   1321 
   1322 void MacroAssemblerARM::ma_bx(Register dest, Assembler::Condition c) {
   1323  as_bx(dest, c);
   1324 }
   1325 
   1326 void MacroAssemblerARM::ma_b(void* target, Assembler::Condition c) {
   1327  // An immediate pool is used for easier patching.
   1328  as_Imm32Pool(pc, uint32_t(target), c);
   1329 }
   1330 
   1331 // This is almost NEVER necessary: we'll basically never be calling a label,
   1332 // except possibly in the crazy bailout-table case.
   1333 void MacroAssemblerARM::ma_bl(Label* dest, Assembler::Condition c) {
   1334  as_bl(dest, c);
   1335 }
   1336 
   1337 void MacroAssemblerARM::ma_blx(Register reg, Assembler::Condition c) {
   1338  as_blx(reg, c);
   1339 }
   1340 
   1341 // VFP/ALU
   1342 void MacroAssemblerARM::ma_vadd(FloatRegister src1, FloatRegister src2,
   1343                                FloatRegister dst) {
   1344  as_vadd(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2));
   1345 }
   1346 
   1347 void MacroAssemblerARM::ma_vadd_f32(FloatRegister src1, FloatRegister src2,
   1348                                    FloatRegister dst) {
   1349  as_vadd(VFPRegister(dst).singleOverlay(), VFPRegister(src1).singleOverlay(),
   1350          VFPRegister(src2).singleOverlay());
   1351 }
   1352 
   1353 void MacroAssemblerARM::ma_vsub(FloatRegister src1, FloatRegister src2,
   1354                                FloatRegister dst) {
   1355  as_vsub(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2));
   1356 }
   1357 
   1358 void MacroAssemblerARM::ma_vsub_f32(FloatRegister src1, FloatRegister src2,
   1359                                    FloatRegister dst) {
   1360  as_vsub(VFPRegister(dst).singleOverlay(), VFPRegister(src1).singleOverlay(),
   1361          VFPRegister(src2).singleOverlay());
   1362 }
   1363 
   1364 void MacroAssemblerARM::ma_vmul(FloatRegister src1, FloatRegister src2,
   1365                                FloatRegister dst) {
   1366  as_vmul(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2));
   1367 }
   1368 
   1369 void MacroAssemblerARM::ma_vmul_f32(FloatRegister src1, FloatRegister src2,
   1370                                    FloatRegister dst) {
   1371  as_vmul(VFPRegister(dst).singleOverlay(), VFPRegister(src1).singleOverlay(),
   1372          VFPRegister(src2).singleOverlay());
   1373 }
   1374 
   1375 void MacroAssemblerARM::ma_vdiv(FloatRegister src1, FloatRegister src2,
   1376                                FloatRegister dst) {
   1377  as_vdiv(VFPRegister(dst), VFPRegister(src1), VFPRegister(src2));
   1378 }
   1379 
   1380 void MacroAssemblerARM::ma_vdiv_f32(FloatRegister src1, FloatRegister src2,
   1381                                    FloatRegister dst) {
   1382  as_vdiv(VFPRegister(dst).singleOverlay(), VFPRegister(src1).singleOverlay(),
   1383          VFPRegister(src2).singleOverlay());
   1384 }
   1385 
   1386 void MacroAssemblerARM::ma_vmov(FloatRegister src, FloatRegister dest,
   1387                                Condition cc) {
   1388  as_vmov(dest, src, cc);
   1389 }
   1390 
   1391 void MacroAssemblerARM::ma_vmov_f32(FloatRegister src, FloatRegister dest,
   1392                                    Condition cc) {
   1393  as_vmov(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(),
   1394          cc);
   1395 }
   1396 
   1397 void MacroAssemblerARM::ma_vneg(FloatRegister src, FloatRegister dest,
   1398                                Condition cc) {
   1399  as_vneg(dest, src, cc);
   1400 }
   1401 
   1402 void MacroAssemblerARM::ma_vneg_f32(FloatRegister src, FloatRegister dest,
   1403                                    Condition cc) {
   1404  as_vneg(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(),
   1405          cc);
   1406 }
   1407 
   1408 void MacroAssemblerARM::ma_vabs(FloatRegister src, FloatRegister dest,
   1409                                Condition cc) {
   1410  as_vabs(dest, src, cc);
   1411 }
   1412 
   1413 void MacroAssemblerARM::ma_vabs_f32(FloatRegister src, FloatRegister dest,
   1414                                    Condition cc) {
   1415  as_vabs(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(),
   1416          cc);
   1417 }
   1418 
   1419 void MacroAssemblerARM::ma_vsqrt(FloatRegister src, FloatRegister dest,
   1420                                 Condition cc) {
   1421  as_vsqrt(dest, src, cc);
   1422 }
   1423 
   1424 void MacroAssemblerARM::ma_vsqrt_f32(FloatRegister src, FloatRegister dest,
   1425                                     Condition cc) {
   1426  as_vsqrt(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(),
   1427           cc);
   1428 }
   1429 
   1430 static inline uint32_t DoubleHighWord(double d) {
   1431  return static_cast<uint32_t>(BitwiseCast<uint64_t>(d) >> 32);
   1432 }
   1433 
   1434 static inline uint32_t DoubleLowWord(double d) {
   1435  return static_cast<uint32_t>(BitwiseCast<uint64_t>(d)) & uint32_t(0xffffffff);
   1436 }
   1437 
   1438 void MacroAssemblerARM::ma_vimm(double value, FloatRegister dest,
   1439                                Condition cc) {
   1440  if (ARMFlags::HasVFPv3()) {
   1441    if (DoubleLowWord(value) == 0) {
   1442      if (DoubleHighWord(value) == 0) {
   1443        // To zero a register, load 1.0, then execute dN <- dN - dN
   1444        as_vimm(dest, VFPImm::One, cc);
   1445        as_vsub(dest, dest, dest, cc);
   1446        return;
   1447      }
   1448 
   1449      VFPImm enc(DoubleHighWord(value));
   1450      if (enc.isValid()) {
   1451        as_vimm(dest, enc, cc);
   1452        return;
   1453      }
   1454    }
   1455  }
   1456  // Fall back to putting the value in a pool.
   1457  as_FImm64Pool(dest, value, cc);
   1458 }
   1459 
   1460 void MacroAssemblerARM::ma_vimm_f32(float value, FloatRegister dest,
   1461                                    Condition cc) {
   1462  VFPRegister vd = VFPRegister(dest).singleOverlay();
   1463  if (ARMFlags::HasVFPv3()) {
   1464    if (IsPositiveZero(value)) {
   1465      // To zero a register, load 1.0, then execute sN <- sN - sN.
   1466      as_vimm(vd, VFPImm::One, cc);
   1467      as_vsub(vd, vd, vd, cc);
   1468      return;
   1469    }
   1470 
   1471    // Note that the vimm immediate float32 instruction encoding differs
   1472    // from the vimm immediate double encoding, but this difference matches
   1473    // the difference in the floating point formats, so it is possible to
   1474    // convert the float32 to a double and then use the double encoding
   1475    // paths. It is still necessary to firstly check that the double low
   1476    // word is zero because some float32 numbers set these bits and this can
   1477    // not be ignored.
   1478    double doubleValue(value);
   1479    if (DoubleLowWord(doubleValue) == 0) {
   1480      VFPImm enc(DoubleHighWord(doubleValue));
   1481      if (enc.isValid()) {
   1482        as_vimm(vd, enc, cc);
   1483        return;
   1484      }
   1485    }
   1486  }
   1487 
   1488  // Fall back to putting the value in a pool.
   1489  as_FImm32Pool(vd, value, cc);
   1490 }
   1491 
   1492 void MacroAssemblerARM::ma_vcmp(FloatRegister src1, FloatRegister src2,
   1493                                Condition cc) {
   1494  as_vcmp(VFPRegister(src1), VFPRegister(src2), cc);
   1495 }
   1496 
   1497 void MacroAssemblerARM::ma_vcmp_f32(FloatRegister src1, FloatRegister src2,
   1498                                    Condition cc) {
   1499  as_vcmp(VFPRegister(src1).singleOverlay(), VFPRegister(src2).singleOverlay(),
   1500          cc);
   1501 }
   1502 
   1503 void MacroAssemblerARM::ma_vcmpz(FloatRegister src1, Condition cc) {
   1504  as_vcmpz(VFPRegister(src1), cc);
   1505 }
   1506 
   1507 void MacroAssemblerARM::ma_vcmpz_f32(FloatRegister src1, Condition cc) {
   1508  as_vcmpz(VFPRegister(src1).singleOverlay(), cc);
   1509 }
   1510 
   1511 void MacroAssemblerARM::ma_vcvt_F64_I32(FloatRegister src, FloatRegister dest,
   1512                                        Condition cc) {
   1513  MOZ_ASSERT(src.isDouble());
   1514  MOZ_ASSERT(dest.isSInt());
   1515  as_vcvt(dest, src, false, cc);
   1516 }
   1517 
   1518 void MacroAssemblerARM::ma_vcvt_F64_U32(FloatRegister src, FloatRegister dest,
   1519                                        Condition cc) {
   1520  MOZ_ASSERT(src.isDouble());
   1521  MOZ_ASSERT(dest.isUInt());
   1522  as_vcvt(dest, src, false, cc);
   1523 }
   1524 
   1525 void MacroAssemblerARM::ma_vcvt_I32_F64(FloatRegister src, FloatRegister dest,
   1526                                        Condition cc) {
   1527  MOZ_ASSERT(src.isSInt());
   1528  MOZ_ASSERT(dest.isDouble());
   1529  as_vcvt(dest, src, false, cc);
   1530 }
   1531 
   1532 void MacroAssemblerARM::ma_vcvt_U32_F64(FloatRegister src, FloatRegister dest,
   1533                                        Condition cc) {
   1534  MOZ_ASSERT(src.isUInt());
   1535  MOZ_ASSERT(dest.isDouble());
   1536  as_vcvt(dest, src, false, cc);
   1537 }
   1538 
   1539 void MacroAssemblerARM::ma_vcvt_F32_I32(FloatRegister src, FloatRegister dest,
   1540                                        Condition cc) {
   1541  MOZ_ASSERT(src.isSingle());
   1542  MOZ_ASSERT(dest.isSInt());
   1543  as_vcvt(VFPRegister(dest).sintOverlay(), VFPRegister(src).singleOverlay(),
   1544          false, cc);
   1545 }
   1546 
   1547 void MacroAssemblerARM::ma_vcvt_F32_U32(FloatRegister src, FloatRegister dest,
   1548                                        Condition cc) {
   1549  MOZ_ASSERT(src.isSingle());
   1550  MOZ_ASSERT(dest.isUInt());
   1551  as_vcvt(VFPRegister(dest).uintOverlay(), VFPRegister(src).singleOverlay(),
   1552          false, cc);
   1553 }
   1554 
   1555 void MacroAssemblerARM::ma_vcvt_I32_F32(FloatRegister src, FloatRegister dest,
   1556                                        Condition cc) {
   1557  MOZ_ASSERT(src.isSInt());
   1558  MOZ_ASSERT(dest.isSingle());
   1559  as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src).sintOverlay(),
   1560          false, cc);
   1561 }
   1562 
   1563 void MacroAssemblerARM::ma_vcvt_U32_F32(FloatRegister src, FloatRegister dest,
   1564                                        Condition cc) {
   1565  MOZ_ASSERT(src.isUInt());
   1566  MOZ_ASSERT(dest.isSingle());
   1567  as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src).uintOverlay(),
   1568          false, cc);
   1569 }
   1570 
   1571 void MacroAssemblerARM::ma_vxfer(FloatRegister src, Register dest,
   1572                                 Condition cc) {
   1573  as_vxfer(dest, InvalidReg, VFPRegister(src).singleOverlay(), FloatToCore, cc);
   1574 }
   1575 
   1576 void MacroAssemblerARM::ma_vxfer(FloatRegister src, Register dest1,
   1577                                 Register dest2, Condition cc) {
   1578  as_vxfer(dest1, dest2, VFPRegister(src), FloatToCore, cc);
   1579 }
   1580 
   1581 void MacroAssemblerARM::ma_vxfer(Register src, FloatRegister dest,
   1582                                 Condition cc) {
   1583  as_vxfer(src, InvalidReg, VFPRegister(dest).singleOverlay(), CoreToFloat, cc);
   1584 }
   1585 
   1586 void MacroAssemblerARM::ma_vxfer(Register src1, Register src2,
   1587                                 FloatRegister dest, Condition cc) {
   1588  as_vxfer(src1, src2, VFPRegister(dest), CoreToFloat, cc);
   1589 }
   1590 
   1591 BufferOffset MacroAssemblerARM::ma_vdtr(LoadStore ls, const Address& addr,
   1592                                        VFPRegister rt,
   1593                                        AutoRegisterScope& scratch,
   1594                                        Condition cc) {
   1595  int off = addr.offset;
   1596  MOZ_ASSERT((off & 3) == 0);
   1597  Register base = addr.base;
   1598  if (off > -1024 && off < 1024) {
   1599    return as_vdtr(ls, rt, Operand(addr).toVFPAddr(), cc);
   1600  }
   1601 
   1602  // We cannot encode this offset in a a single ldr. Try to encode it as an
   1603  // add scratch, base, imm; ldr dest, [scratch, +offset].
   1604  int bottom = off & (0xff << 2);
   1605  int neg_bottom = (0x100 << 2) - bottom;
   1606  // At this point, both off - bottom and off + neg_bottom will be
   1607  // reasonable-ish quantities.
   1608  //
   1609  // Note a neg_bottom of 0x400 can not be encoded as an immediate negative
   1610  // offset in the instruction and this occurs when bottom is zero, so this
   1611  // case is guarded against below.
   1612  if (off < 0) {
   1613    // sub_off = bottom - off
   1614    Operand2 sub_off = Imm8(-(off - bottom));
   1615    if (!sub_off.invalid()) {
   1616      // - sub_off = off - bottom
   1617      as_sub(scratch, base, sub_off, LeaveCC, cc);
   1618      return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(bottom)), cc);
   1619    }
   1620    // sub_off = -neg_bottom - off
   1621    sub_off = Imm8(-(off + neg_bottom));
   1622    if (!sub_off.invalid() && bottom != 0) {
   1623      // Guarded against by: bottom != 0
   1624      MOZ_ASSERT(neg_bottom < 0x400);
   1625      // - sub_off = neg_bottom + off
   1626      as_sub(scratch, base, sub_off, LeaveCC, cc);
   1627      return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(-neg_bottom)), cc);
   1628    }
   1629  } else {
   1630    // sub_off = off - bottom
   1631    Operand2 sub_off = Imm8(off - bottom);
   1632    if (!sub_off.invalid()) {
   1633      // sub_off = off - bottom
   1634      as_add(scratch, base, sub_off, LeaveCC, cc);
   1635      return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(bottom)), cc);
   1636    }
   1637    // sub_off = neg_bottom + off
   1638    sub_off = Imm8(off + neg_bottom);
   1639    if (!sub_off.invalid() && bottom != 0) {
   1640      // Guarded against by: bottom != 0
   1641      MOZ_ASSERT(neg_bottom < 0x400);
   1642      // sub_off = neg_bottom + off
   1643      as_add(scratch, base, sub_off, LeaveCC, cc);
   1644      return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(-neg_bottom)), cc);
   1645    }
   1646  }
   1647 
   1648  // Safe to use scratch as dest, since ma_add() overwrites dest at the end
   1649  // and can't use it as internal scratch since it may also == base.
   1650  ma_add(base, Imm32(off), scratch, scratch, LeaveCC, cc);
   1651  return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(0)), cc);
   1652 }
   1653 
   1654 BufferOffset MacroAssemblerARM::ma_vldr(VFPAddr addr, VFPRegister dest,
   1655                                        Condition cc) {
   1656  return as_vdtr(IsLoad, dest, addr, cc);
   1657 }
   1658 
   1659 BufferOffset MacroAssemblerARM::ma_vldr(const Address& addr, VFPRegister dest,
   1660                                        AutoRegisterScope& scratch,
   1661                                        Condition cc) {
   1662  return ma_vdtr(IsLoad, addr, dest, scratch, cc);
   1663 }
   1664 
   1665 BufferOffset MacroAssemblerARM::ma_vldr(VFPRegister src, Register base,
   1666                                        Register index,
   1667                                        AutoRegisterScope& scratch,
   1668                                        int32_t shift, Condition cc) {
   1669  as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
   1670  return as_vdtr(IsLoad, src, Operand(Address(scratch, 0)).toVFPAddr(), cc);
   1671 }
   1672 
   1673 BufferOffset MacroAssemblerARM::ma_vstr(VFPRegister src, VFPAddr addr,
   1674                                        Condition cc) {
   1675  return as_vdtr(IsStore, src, addr, cc);
   1676 }
   1677 
   1678 BufferOffset MacroAssemblerARM::ma_vstr(VFPRegister src, const Address& addr,
   1679                                        AutoRegisterScope& scratch,
   1680                                        Condition cc) {
   1681  return ma_vdtr(IsStore, addr, src, scratch, cc);
   1682 }
   1683 
   1684 BufferOffset MacroAssemblerARM::ma_vstr(
   1685    VFPRegister src, Register base, Register index, AutoRegisterScope& scratch,
   1686    AutoRegisterScope& scratch2, int32_t shift, int32_t offset, Condition cc) {
   1687  as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
   1688  return ma_vstr(src, Address(scratch, offset), scratch2, cc);
   1689 }
   1690 
   1691 // Without an offset, no second scratch register is necessary.
   1692 BufferOffset MacroAssemblerARM::ma_vstr(VFPRegister src, Register base,
   1693                                        Register index,
   1694                                        AutoRegisterScope& scratch,
   1695                                        int32_t shift, Condition cc) {
   1696  as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
   1697  return as_vdtr(IsStore, src, Operand(Address(scratch, 0)).toVFPAddr(), cc);
   1698 }
   1699 
   1700 bool MacroAssemblerARMCompat::buildOOLFakeExitFrame(void* fakeReturnAddr) {
   1701  asMasm().Push(FrameDescriptor(FrameType::IonJS));  // descriptor_
   1702  asMasm().Push(ImmPtr(fakeReturnAddr));
   1703  asMasm().Push(FramePointer);
   1704  return true;
   1705 }
   1706 
   1707 void MacroAssemblerARMCompat::move32(Imm32 imm, Register dest) {
   1708  ma_mov(imm, dest);
   1709 }
   1710 
   1711 void MacroAssemblerARMCompat::move32(Register src, Register dest) {
   1712  ma_mov(src, dest);
   1713 }
   1714 
   1715 void MacroAssemblerARMCompat::movePtr(Register src, Register dest) {
   1716  ma_mov(src, dest);
   1717 }
   1718 
   1719 void MacroAssemblerARMCompat::movePtr(ImmWord imm, Register dest) {
   1720  ma_mov(Imm32(imm.value), dest);
   1721 }
   1722 
   1723 void MacroAssemblerARMCompat::movePtr(ImmGCPtr imm, Register dest) {
   1724  ma_mov(imm, dest);
   1725 }
   1726 
   1727 void MacroAssemblerARMCompat::movePtr(ImmPtr imm, Register dest) {
   1728  movePtr(ImmWord(uintptr_t(imm.value)), dest);
   1729 }
   1730 
   1731 void MacroAssemblerARMCompat::movePtr(wasm::SymbolicAddress imm,
   1732                                      Register dest) {
   1733  append(wasm::SymbolicAccess(CodeOffset(currentOffset()), imm));
   1734  ma_movPatchable(Imm32(-1), dest, Always);
   1735 }
   1736 
   1737 FaultingCodeOffset MacroAssemblerARMCompat::load8ZeroExtend(
   1738    const Address& address, Register dest) {
   1739  ScratchRegisterScope scratch(asMasm());
   1740  BufferOffset offset = ma_dataTransferN(IsLoad, 8, false, address.base,
   1741                                         Imm32(address.offset), dest, scratch);
   1742  return FaultingCodeOffset(offset.getOffset());
   1743 }
   1744 
   1745 FaultingCodeOffset MacroAssemblerARMCompat::load8ZeroExtend(
   1746    const BaseIndex& src, Register dest) {
   1747  Register base = src.base;
   1748  uint32_t scale = Imm32::ShiftOf(src.scale).value;
   1749 
   1750  ScratchRegisterScope scratch(asMasm());
   1751  SecondScratchRegisterScope scratch2(asMasm());
   1752 
   1753  FaultingCodeOffset fco;
   1754  if (src.offset == 0) {
   1755    fco = ma_ldrb(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest);
   1756  } else {
   1757    ma_add(base, Imm32(src.offset), scratch, scratch2);
   1758    fco =
   1759        ma_ldrb(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest);
   1760  }
   1761  return fco;
   1762 }
   1763 
   1764 FaultingCodeOffset MacroAssemblerARMCompat::load8SignExtend(
   1765    const Address& address, Register dest) {
   1766  ScratchRegisterScope scratch(asMasm());
   1767  BufferOffset offset = ma_dataTransferN(IsLoad, 8, true, address.base,
   1768                                         Imm32(address.offset), dest, scratch);
   1769  return FaultingCodeOffset(offset.getOffset());
   1770 }
   1771 
   1772 FaultingCodeOffset MacroAssemblerARMCompat::load8SignExtend(
   1773    const BaseIndex& src, Register dest) {
   1774  Register index = src.index;
   1775 
   1776  ScratchRegisterScope scratch(asMasm());
   1777  SecondScratchRegisterScope scratch2(asMasm());
   1778 
   1779  // ARMv7 does not have LSL on an index register with an extended load.
   1780  if (src.scale != TimesOne) {
   1781    ma_lsl(Imm32::ShiftOf(src.scale), index, scratch);
   1782    index = scratch;
   1783  }
   1784 
   1785  if (src.offset != 0) {
   1786    if (index != scratch) {
   1787      ma_mov(index, scratch);
   1788      index = scratch;
   1789    }
   1790    ma_add(Imm32(src.offset), index, scratch2);
   1791  }
   1792  return ma_ldrsb(EDtrAddr(src.base, EDtrOffReg(index)), dest);
   1793 }
   1794 
   1795 FaultingCodeOffset MacroAssemblerARMCompat::load16ZeroExtend(
   1796    const Address& address, Register dest) {
   1797  ScratchRegisterScope scratch(asMasm());
   1798  BufferOffset offset = ma_dataTransferN(IsLoad, 16, false, address.base,
   1799                                         Imm32(address.offset), dest, scratch);
   1800  return FaultingCodeOffset(offset.getOffset());
   1801 }
   1802 
   1803 FaultingCodeOffset MacroAssemblerARMCompat::load16ZeroExtend(
   1804    const BaseIndex& src, Register dest) {
   1805  Register index = src.index;
   1806 
   1807  ScratchRegisterScope scratch(asMasm());
   1808  SecondScratchRegisterScope scratch2(asMasm());
   1809 
   1810  // ARMv7 does not have LSL on an index register with an extended load.
   1811  if (src.scale != TimesOne) {
   1812    ma_lsl(Imm32::ShiftOf(src.scale), index, scratch);
   1813    index = scratch;
   1814  }
   1815 
   1816  if (src.offset != 0) {
   1817    if (index != scratch) {
   1818      ma_mov(index, scratch);
   1819      index = scratch;
   1820    }
   1821    ma_add(Imm32(src.offset), index, scratch2);
   1822  }
   1823  return ma_ldrh(EDtrAddr(src.base, EDtrOffReg(index)), dest);
   1824 }
   1825 
   1826 FaultingCodeOffset MacroAssemblerARMCompat::load16SignExtend(
   1827    const Address& address, Register dest) {
   1828  ScratchRegisterScope scratch(asMasm());
   1829  BufferOffset offset = ma_dataTransferN(IsLoad, 16, true, address.base,
   1830                                         Imm32(address.offset), dest, scratch);
   1831  return FaultingCodeOffset(offset.getOffset());
   1832 }
   1833 
   1834 FaultingCodeOffset MacroAssemblerARMCompat::load16SignExtend(
   1835    const BaseIndex& src, Register dest) {
   1836  Register index = src.index;
   1837 
   1838  ScratchRegisterScope scratch(asMasm());
   1839  SecondScratchRegisterScope scratch2(asMasm());
   1840 
   1841  // We don't have LSL on index register yet.
   1842  if (src.scale != TimesOne) {
   1843    ma_lsl(Imm32::ShiftOf(src.scale), index, scratch);
   1844    index = scratch;
   1845  }
   1846 
   1847  if (src.offset != 0) {
   1848    if (index != scratch) {
   1849      ma_mov(index, scratch);
   1850      index = scratch;
   1851    }
   1852    ma_add(Imm32(src.offset), index, scratch2);
   1853  }
   1854  return ma_ldrsh(EDtrAddr(src.base, EDtrOffReg(index)), dest);
   1855 }
   1856 
   1857 FaultingCodeOffset MacroAssemblerARMCompat::load32(const Address& address,
   1858                                                   Register dest) {
   1859  return loadPtr(address, dest);
   1860 }
   1861 
   1862 FaultingCodeOffset MacroAssemblerARMCompat::load32(const BaseIndex& address,
   1863                                                   Register dest) {
   1864  return loadPtr(address, dest);
   1865 }
   1866 
   1867 void MacroAssemblerARMCompat::load32(AbsoluteAddress address, Register dest) {
   1868  loadPtr(address, dest);
   1869 }
   1870 
   1871 FaultingCodeOffset MacroAssemblerARMCompat::loadPtr(const Address& address,
   1872                                                    Register dest) {
   1873  ScratchRegisterScope scratch(asMasm());
   1874  return ma_ldr(address, dest, scratch);
   1875 }
   1876 
   1877 FaultingCodeOffset MacroAssemblerARMCompat::loadPtr(const BaseIndex& src,
   1878                                                    Register dest) {
   1879  Register base = src.base;
   1880  uint32_t scale = Imm32::ShiftOf(src.scale).value;
   1881 
   1882  ScratchRegisterScope scratch(asMasm());
   1883  SecondScratchRegisterScope scratch2(asMasm());
   1884 
   1885  FaultingCodeOffset fco;
   1886  if (src.offset != 0) {
   1887    ma_add(base, Imm32(src.offset), scratch, scratch2);
   1888    fco = ma_ldr(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest);
   1889  } else {
   1890    fco = ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest);
   1891  }
   1892  return fco;
   1893 }
   1894 
   1895 void MacroAssemblerARMCompat::loadPtr(AbsoluteAddress address, Register dest) {
   1896  MOZ_ASSERT(dest != pc);  // Use dest as a scratch register.
   1897  movePtr(ImmWord(uintptr_t(address.addr)), dest);
   1898  loadPtr(Address(dest, 0), dest);
   1899 }
   1900 
   1901 void MacroAssemblerARMCompat::loadPtr(wasm::SymbolicAddress address,
   1902                                      Register dest) {
   1903  MOZ_ASSERT(dest != pc);  // Use dest as a scratch register.
   1904  movePtr(address, dest);
   1905  loadPtr(Address(dest, 0), dest);
   1906 }
   1907 
   1908 void MacroAssemblerARMCompat::loadPrivate(const Address& address,
   1909                                          Register dest) {
   1910  ScratchRegisterScope scratch(asMasm());
   1911  ma_ldr(ToPayload(address), dest, scratch);
   1912 }
   1913 
   1914 FaultingCodeOffset MacroAssemblerARMCompat::loadDouble(const Address& address,
   1915                                                       FloatRegister dest) {
   1916  ScratchRegisterScope scratch(asMasm());
   1917  BufferOffset offset = ma_vldr(address, dest, scratch);
   1918  return FaultingCodeOffset(offset.getOffset());
   1919 }
   1920 
   1921 FaultingCodeOffset MacroAssemblerARMCompat::loadDouble(const BaseIndex& src,
   1922                                                       FloatRegister dest) {
   1923  // VFP instructions don't even support register Base + register Index modes,
   1924  // so just add the index, then handle the offset like normal.
   1925  Register base = src.base;
   1926  Register index = src.index;
   1927  uint32_t scale = Imm32::ShiftOf(src.scale).value;
   1928  int32_t offset = src.offset;
   1929 
   1930  ScratchRegisterScope scratch(asMasm());
   1931  SecondScratchRegisterScope scratch2(asMasm());
   1932 
   1933  as_add(scratch, base, lsl(index, scale));
   1934  BufferOffset boffset = ma_vldr(Address(scratch, offset), dest, scratch2);
   1935  return FaultingCodeOffset(boffset.getOffset());
   1936 }
   1937 
   1938 FaultingCodeOffset MacroAssemblerARMCompat::loadFloat32(const Address& address,
   1939                                                        FloatRegister dest) {
   1940  ScratchRegisterScope scratch(asMasm());
   1941  BufferOffset offset =
   1942      ma_vldr(address, VFPRegister(dest).singleOverlay(), scratch);
   1943  return FaultingCodeOffset(offset.getOffset());
   1944 }
   1945 
   1946 FaultingCodeOffset MacroAssemblerARMCompat::loadFloat32(const BaseIndex& src,
   1947                                                        FloatRegister dest) {
   1948  // VFP instructions don't even support register Base + register Index modes,
   1949  // so just add the index, then handle the offset like normal.
   1950  Register base = src.base;
   1951  Register index = src.index;
   1952  uint32_t scale = Imm32::ShiftOf(src.scale).value;
   1953  int32_t offset = src.offset;
   1954 
   1955  ScratchRegisterScope scratch(asMasm());
   1956  SecondScratchRegisterScope scratch2(asMasm());
   1957 
   1958  as_add(scratch, base, lsl(index, scale));
   1959  BufferOffset boffset = ma_vldr(Address(scratch, offset),
   1960                                 VFPRegister(dest).singleOverlay(), scratch2);
   1961  return FaultingCodeOffset(boffset.getOffset());
   1962 }
   1963 
   1964 FaultingCodeOffset MacroAssemblerARMCompat::loadFloat16(const Address& address,
   1965                                                        FloatRegister dest,
   1966                                                        Register scratch) {
   1967  auto fco = load16ZeroExtend(address, scratch);
   1968  ma_vxfer(scratch, dest);
   1969  return fco;
   1970 }
   1971 
   1972 FaultingCodeOffset MacroAssemblerARMCompat::loadFloat16(const BaseIndex& src,
   1973                                                        FloatRegister dest,
   1974                                                        Register scratch) {
   1975  auto fco = load16ZeroExtend(src, scratch);
   1976  ma_vxfer(scratch, dest);
   1977  return fco;
   1978 }
   1979 
   1980 void MacroAssemblerARMCompat::store8(Imm32 imm, const Address& address) {
   1981  SecondScratchRegisterScope scratch2(asMasm());
   1982  ma_mov(imm, scratch2);
   1983  store8(scratch2, address);
   1984 }
   1985 
   1986 FaultingCodeOffset MacroAssemblerARMCompat::store8(Register src,
   1987                                                   const Address& address) {
   1988  ScratchRegisterScope scratch(asMasm());
   1989  BufferOffset offset = ma_dataTransferN(IsStore, 8, false, address.base,
   1990                                         Imm32(address.offset), src, scratch);
   1991  return FaultingCodeOffset(offset.getOffset());
   1992 }
   1993 
   1994 void MacroAssemblerARMCompat::store8(Imm32 imm, const BaseIndex& dest) {
   1995  Register base = dest.base;
   1996  uint32_t scale = Imm32::ShiftOf(dest.scale).value;
   1997 
   1998  ScratchRegisterScope scratch(asMasm());
   1999  SecondScratchRegisterScope scratch2(asMasm());
   2000 
   2001  if (dest.offset != 0) {
   2002    ma_add(base, Imm32(dest.offset), scratch, scratch2);
   2003    ma_mov(imm, scratch2);
   2004    ma_strb(scratch2, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale)));
   2005  } else {
   2006    ma_mov(imm, scratch2);
   2007    ma_strb(scratch2, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
   2008  }
   2009 }
   2010 
   2011 FaultingCodeOffset MacroAssemblerARMCompat::store8(Register src,
   2012                                                   const BaseIndex& dest) {
   2013  Register base = dest.base;
   2014  uint32_t scale = Imm32::ShiftOf(dest.scale).value;
   2015 
   2016  ScratchRegisterScope scratch(asMasm());
   2017  SecondScratchRegisterScope scratch2(asMasm());
   2018 
   2019  FaultingCodeOffset fco;
   2020  if (dest.offset != 0) {
   2021    ma_add(base, Imm32(dest.offset), scratch, scratch2);
   2022    fco =
   2023        ma_strb(src, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale)));
   2024  } else {
   2025    fco = ma_strb(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
   2026  }
   2027  return fco;
   2028 }
   2029 
   2030 void MacroAssemblerARMCompat::store16(Imm32 imm, const Address& address) {
   2031  SecondScratchRegisterScope scratch2(asMasm());
   2032  ma_mov(imm, scratch2);
   2033  store16(scratch2, address);
   2034 }
   2035 
   2036 FaultingCodeOffset MacroAssemblerARMCompat::store16(Register src,
   2037                                                    const Address& address) {
   2038  ScratchRegisterScope scratch(asMasm());
   2039  BufferOffset offset = ma_dataTransferN(IsStore, 16, false, address.base,
   2040                                         Imm32(address.offset), src, scratch);
   2041  return FaultingCodeOffset(offset.getOffset());
   2042 }
   2043 
   2044 void MacroAssemblerARMCompat::store16(Imm32 imm, const BaseIndex& dest) {
   2045  Register index = dest.index;
   2046 
   2047  ScratchRegisterScope scratch(asMasm());
   2048  SecondScratchRegisterScope scratch2(asMasm());
   2049 
   2050  // We don't have LSL on index register yet.
   2051  if (dest.scale != TimesOne) {
   2052    ma_lsl(Imm32::ShiftOf(dest.scale), index, scratch);
   2053    index = scratch;
   2054  }
   2055 
   2056  if (dest.offset != 0) {
   2057    ma_add(index, Imm32(dest.offset), scratch, scratch2);
   2058    index = scratch;
   2059  }
   2060 
   2061  ma_mov(imm, scratch2);
   2062  ma_strh(scratch2, EDtrAddr(dest.base, EDtrOffReg(index)));
   2063 }
   2064 
   2065 FaultingCodeOffset MacroAssemblerARMCompat::store16(Register src,
   2066                                                    const BaseIndex& address) {
   2067  Register index = address.index;
   2068 
   2069  ScratchRegisterScope scratch(asMasm());
   2070  SecondScratchRegisterScope scratch2(asMasm());
   2071 
   2072  // We don't have LSL on index register yet.
   2073  if (address.scale != TimesOne) {
   2074    ma_lsl(Imm32::ShiftOf(address.scale), index, scratch);
   2075    index = scratch;
   2076  }
   2077 
   2078  if (address.offset != 0) {
   2079    ma_add(index, Imm32(address.offset), scratch, scratch2);
   2080    index = scratch;
   2081  }
   2082  return ma_strh(src, EDtrAddr(address.base, EDtrOffReg(index)));
   2083 }
   2084 
   2085 void MacroAssemblerARMCompat::store32(Register src, AbsoluteAddress address) {
   2086  storePtr(src, address);
   2087 }
   2088 
   2089 FaultingCodeOffset MacroAssemblerARMCompat::store32(Register src,
   2090                                                    const Address& address) {
   2091  return storePtr(src, address);
   2092 }
   2093 
   2094 void MacroAssemblerARMCompat::store32(Imm32 src, const Address& address) {
   2095  ScratchRegisterScope scratch(asMasm());
   2096  SecondScratchRegisterScope scratch2(asMasm());
   2097  move32(src, scratch);
   2098  ma_str(scratch, address, scratch2);
   2099 }
   2100 
   2101 void MacroAssemblerARMCompat::store32(Imm32 imm, const BaseIndex& dest) {
   2102  Register base = dest.base;
   2103  uint32_t scale = Imm32::ShiftOf(dest.scale).value;
   2104 
   2105  ScratchRegisterScope scratch(asMasm());
   2106  SecondScratchRegisterScope scratch2(asMasm());
   2107 
   2108  if (dest.offset != 0) {
   2109    ma_add(base, Imm32(dest.offset), scratch, scratch2);
   2110    ma_mov(imm, scratch2);
   2111    ma_str(scratch2, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale)));
   2112  } else {
   2113    ma_mov(imm, scratch);
   2114    ma_str(scratch, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
   2115  }
   2116 }
   2117 
   2118 FaultingCodeOffset MacroAssemblerARMCompat::store32(Register src,
   2119                                                    const BaseIndex& dest) {
   2120  Register base = dest.base;
   2121  uint32_t scale = Imm32::ShiftOf(dest.scale).value;
   2122 
   2123  ScratchRegisterScope scratch(asMasm());
   2124  SecondScratchRegisterScope scratch2(asMasm());
   2125 
   2126  FaultingCodeOffset fco;
   2127  if (dest.offset != 0) {
   2128    ma_add(base, Imm32(dest.offset), scratch, scratch2);
   2129    fco = ma_str(src, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale)));
   2130  } else {
   2131    fco = ma_str(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
   2132  }
   2133  return fco;
   2134 }
   2135 
   2136 void MacroAssemblerARMCompat::storePtr(ImmWord imm, const Address& address) {
   2137  store32(Imm32(imm.value), address);
   2138 }
   2139 
   2140 void MacroAssemblerARMCompat::storePtr(ImmWord imm, const BaseIndex& address) {
   2141  store32(Imm32(imm.value), address);
   2142 }
   2143 
   2144 void MacroAssemblerARMCompat::storePtr(ImmPtr imm, const Address& address) {
   2145  store32(Imm32(uintptr_t(imm.value)), address);
   2146 }
   2147 
   2148 void MacroAssemblerARMCompat::storePtr(ImmPtr imm, const BaseIndex& address) {
   2149  store32(Imm32(uintptr_t(imm.value)), address);
   2150 }
   2151 
   2152 void MacroAssemblerARMCompat::storePtr(ImmGCPtr imm, const Address& address) {
   2153  ScratchRegisterScope scratch(asMasm());
   2154  SecondScratchRegisterScope scratch2(asMasm());
   2155  ma_mov(imm, scratch);
   2156  ma_str(scratch, address, scratch2);
   2157 }
   2158 
   2159 void MacroAssemblerARMCompat::storePtr(ImmGCPtr imm, const BaseIndex& address) {
   2160  Register base = address.base;
   2161  uint32_t scale = Imm32::ShiftOf(address.scale).value;
   2162 
   2163  ScratchRegisterScope scratch(asMasm());
   2164  SecondScratchRegisterScope scratch2(asMasm());
   2165 
   2166  if (address.offset != 0) {
   2167    ma_add(base, Imm32(address.offset), scratch, scratch2);
   2168    ma_mov(imm, scratch2);
   2169    ma_str(scratch2,
   2170           DTRAddr(scratch, DtrRegImmShift(address.index, LSL, scale)));
   2171  } else {
   2172    ma_mov(imm, scratch);
   2173    ma_str(scratch, DTRAddr(base, DtrRegImmShift(address.index, LSL, scale)));
   2174  }
   2175 }
   2176 
   2177 FaultingCodeOffset MacroAssemblerARMCompat::storePtr(Register src,
   2178                                                     const Address& address) {
   2179  SecondScratchRegisterScope scratch2(asMasm());
   2180  return ma_str(src, address, scratch2);
   2181 }
   2182 
   2183 FaultingCodeOffset MacroAssemblerARMCompat::storePtr(Register src,
   2184                                                     const BaseIndex& address) {
   2185  return store32(src, address);
   2186 }
   2187 
   2188 void MacroAssemblerARMCompat::storePtr(Register src, AbsoluteAddress dest) {
   2189  ScratchRegisterScope scratch(asMasm());
   2190  movePtr(ImmWord(uintptr_t(dest.addr)), scratch);
   2191  ma_str(src, DTRAddr(scratch, DtrOffImm(0)));
   2192 }
   2193 
   2194 // Note: this function clobbers the input register.
   2195 void MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) {
   2196  ScratchDoubleScope scratchDouble(*this);
   2197  MOZ_ASSERT(input != scratchDouble);
   2198 
   2199  Label done;
   2200 
   2201  // Set to zero if NaN.
   2202  compareDouble(input, NoVFPRegister);
   2203  ma_mov(Imm32(0), output, VFP_Unordered);
   2204  ma_b(&done, VFP_Unordered);
   2205 
   2206  // Do the conversion to an integer.
   2207  as_vcvt(VFPRegister(scratchDouble).uintOverlay(), VFPRegister(input));
   2208 
   2209  // Copy the converted value out.
   2210  as_vxfer(output, InvalidReg, scratchDouble, FloatToCore);
   2211 
   2212  // Clamp to 255.
   2213  as_cmp(output, Imm8(0xff));
   2214  ma_mov(Imm32(0xff), output, Above);
   2215  ma_b(&done, AboveOrEqual);
   2216 
   2217  // Convert it back to see if we got the same value back.
   2218  as_vcvt(scratchDouble, VFPRegister(scratchDouble).uintOverlay());
   2219  ma_vsub(input, scratchDouble, input);
   2220 
   2221  loadConstantDouble(0.5, scratchDouble);
   2222 
   2223  // Do the check.
   2224  compareDouble(input, scratchDouble);
   2225 
   2226  // Round up if > 0.5.
   2227  as_add(output, output, Imm8(1), LeaveCC, VFP_GreaterThan);
   2228 
   2229  // Round up if == 0.5 and output is odd.
   2230  as_add(output, output, Imm8(1), LeaveCC, VFP_Equal);
   2231  as_bic(output, output, Imm8(1), LeaveCC, VFP_Equal);
   2232 
   2233  bind(&done);
   2234 }
   2235 
   2236 void MacroAssemblerARMCompat::cmp32(Register lhs, Imm32 rhs) {
   2237  ScratchRegisterScope scratch(asMasm());
   2238  ma_cmp(lhs, rhs, scratch);
   2239 }
   2240 
   2241 void MacroAssemblerARMCompat::cmp32(Register lhs, Register rhs) {
   2242  ma_cmp(lhs, rhs);
   2243 }
   2244 
   2245 void MacroAssemblerARMCompat::cmp32(const Address& lhs, Imm32 rhs) {
   2246  ScratchRegisterScope scratch(asMasm());
   2247  SecondScratchRegisterScope scratch2(asMasm());
   2248  ma_ldr(lhs, scratch, scratch2);
   2249  ma_cmp(scratch, rhs, scratch2);
   2250 }
   2251 
   2252 void MacroAssemblerARMCompat::cmp32(const Address& lhs, Register rhs) {
   2253  ScratchRegisterScope scratch(asMasm());
   2254  SecondScratchRegisterScope scratch2(asMasm());
   2255  ma_ldr(lhs, scratch, scratch2);
   2256  ma_cmp(scratch, rhs);
   2257 }
   2258 
   2259 void MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmWord rhs) {
   2260  cmp32(lhs, Imm32(rhs.value));
   2261 }
   2262 
   2263 void MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmPtr rhs) {
   2264  cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
   2265 }
   2266 
   2267 void MacroAssemblerARMCompat::cmpPtr(Register lhs, Register rhs) {
   2268  ma_cmp(lhs, rhs);
   2269 }
   2270 
   2271 void MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmGCPtr rhs) {
   2272  ScratchRegisterScope scratch(asMasm());
   2273  ma_cmp(lhs, rhs, scratch);
   2274 }
   2275 
   2276 void MacroAssemblerARMCompat::cmpPtr(Register lhs, Imm32 rhs) {
   2277  cmp32(lhs, rhs);
   2278 }
   2279 
   2280 void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, Register rhs) {
   2281  ScratchRegisterScope scratch(asMasm());
   2282  SecondScratchRegisterScope scratch2(asMasm());
   2283  ma_ldr(lhs, scratch, scratch2);
   2284  ma_cmp(scratch, rhs);
   2285 }
   2286 
   2287 void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmWord rhs) {
   2288  ScratchRegisterScope scratch(asMasm());
   2289  SecondScratchRegisterScope scratch2(asMasm());
   2290  ma_ldr(lhs, scratch, scratch2);
   2291  ma_cmp(scratch, Imm32(rhs.value), scratch2);
   2292 }
   2293 
   2294 void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmPtr rhs) {
   2295  cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
   2296 }
   2297 
   2298 void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmGCPtr rhs) {
   2299  ScratchRegisterScope scratch(asMasm());
   2300  SecondScratchRegisterScope scratch2(asMasm());
   2301  ma_ldr(lhs, scratch, scratch2);
   2302  ma_cmp(scratch, rhs, scratch2);
   2303 }
   2304 
   2305 void MacroAssemblerARMCompat::cmpPtr(const Address& lhs, Imm32 rhs) {
   2306  ScratchRegisterScope scratch(asMasm());
   2307  SecondScratchRegisterScope scratch2(asMasm());
   2308  ma_ldr(lhs, scratch, scratch2);
   2309  ma_cmp(scratch, rhs, scratch2);
   2310 }
   2311 
   2312 void MacroAssemblerARMCompat::setStackArg(Register reg, uint32_t arg) {
   2313  ScratchRegisterScope scratch(asMasm());
   2314  ma_dataTransferN(IsStore, 32, true, sp, Imm32(arg * sizeof(intptr_t)), reg,
   2315                   scratch);
   2316 }
   2317 
   2318 void MacroAssemblerARMCompat::minMaxDouble(FloatRegister srcDest,
   2319                                           FloatRegister second, bool canBeNaN,
   2320                                           bool isMax) {
   2321  FloatRegister first = srcDest;
   2322 
   2323  Label nan, equal, returnSecond, done;
   2324 
   2325  Assembler::Condition cond = isMax ? Assembler::VFP_LessThanOrEqual
   2326                                    : Assembler::VFP_GreaterThanOrEqual;
   2327 
   2328  compareDouble(first, second);
   2329  // First or second is NaN, result is NaN.
   2330  ma_b(&nan, Assembler::VFP_Unordered);
   2331  // Make sure we handle -0 and 0 right.
   2332  ma_b(&equal, Assembler::VFP_Equal);
   2333  ma_b(&returnSecond, cond);
   2334  ma_b(&done);
   2335 
   2336  // Check for zero.
   2337  bind(&equal);
   2338  compareDouble(first, NoVFPRegister);
   2339  // First wasn't 0 or -0, so just return it.
   2340  ma_b(&done, Assembler::VFP_NotEqualOrUnordered);
   2341  // So now both operands are either -0 or 0.
   2342  if (isMax) {
   2343    // -0 + -0 = -0 and -0 + 0 = 0.
   2344    ma_vadd(second, first, first);
   2345  } else {
   2346    ma_vneg(first, first);
   2347    ma_vsub(first, second, first);
   2348    ma_vneg(first, first);
   2349  }
   2350  ma_b(&done);
   2351 
   2352  bind(&nan);
   2353  // If the first argument is the NaN, return it; otherwise return the second
   2354  // operand.
   2355  compareDouble(first, first);
   2356  ma_vmov(first, srcDest, Assembler::VFP_Unordered);
   2357  ma_b(&done, Assembler::VFP_Unordered);
   2358 
   2359  bind(&returnSecond);
   2360  ma_vmov(second, srcDest);
   2361 
   2362  bind(&done);
   2363 }
   2364 
   2365 void MacroAssemblerARMCompat::minMax32(Register lhs, Register rhs,
   2366                                       Register dest, bool isMax) {
   2367  if (rhs == dest) {
   2368    std::swap(lhs, rhs);
   2369  }
   2370 
   2371  auto cond = isMax ? Assembler::LessThan : Assembler::GreaterThan;
   2372  if (lhs != dest) {
   2373    move32(lhs, dest);
   2374  }
   2375  cmp32(lhs, rhs);
   2376  ma_mov(rhs, dest, LeaveCC, cond);
   2377 }
   2378 
   2379 void MacroAssemblerARMCompat::minMax32(Register lhs, Imm32 rhs, Register dest,
   2380                                       bool isMax) {
   2381  // We need a scratch register when |rhs| can't be encoded in the compare
   2382  // instruction.
   2383  if (Imm8(rhs.value).invalid() && Imm8(~rhs.value).invalid()) {
   2384    ScratchRegisterScope scratch(asMasm());
   2385    move32(rhs, scratch);
   2386    minMax32(lhs, scratch, dest, isMax);
   2387    return;
   2388  }
   2389 
   2390  auto cond = isMax ? Assembler::LessThan : Assembler::GreaterThan;
   2391  if (lhs != dest) {
   2392    move32(lhs, dest);
   2393  }
   2394  cmp32(lhs, rhs);
   2395  ma_mov(rhs, dest, cond);
   2396 }
   2397 
   2398 void MacroAssemblerARMCompat::minMaxFloat32(FloatRegister srcDest,
   2399                                            FloatRegister second, bool canBeNaN,
   2400                                            bool isMax) {
   2401  FloatRegister first = srcDest;
   2402 
   2403  Label nan, equal, returnSecond, done;
   2404 
   2405  Assembler::Condition cond = isMax ? Assembler::VFP_LessThanOrEqual
   2406                                    : Assembler::VFP_GreaterThanOrEqual;
   2407 
   2408  compareFloat(first, second);
   2409  // First or second is NaN, result is NaN.
   2410  ma_b(&nan, Assembler::VFP_Unordered);
   2411  // Make sure we handle -0 and 0 right.
   2412  ma_b(&equal, Assembler::VFP_Equal);
   2413  ma_b(&returnSecond, cond);
   2414  ma_b(&done);
   2415 
   2416  // Check for zero.
   2417  bind(&equal);
   2418  compareFloat(first, NoVFPRegister);
   2419  // First wasn't 0 or -0, so just return it.
   2420  ma_b(&done, Assembler::VFP_NotEqualOrUnordered);
   2421  // So now both operands are either -0 or 0.
   2422  if (isMax) {
   2423    // -0 + -0 = -0 and -0 + 0 = 0.
   2424    ma_vadd_f32(second, first, first);
   2425  } else {
   2426    ma_vneg_f32(first, first);
   2427    ma_vsub_f32(first, second, first);
   2428    ma_vneg_f32(first, first);
   2429  }
   2430  ma_b(&done);
   2431 
   2432  bind(&nan);
   2433  // See comment in minMaxDouble.
   2434  compareFloat(first, first);
   2435  ma_vmov_f32(first, srcDest, Assembler::VFP_Unordered);
   2436  ma_b(&done, Assembler::VFP_Unordered);
   2437 
   2438  bind(&returnSecond);
   2439  ma_vmov_f32(second, srcDest);
   2440 
   2441  bind(&done);
   2442 }
   2443 
   2444 void MacroAssemblerARMCompat::compareDouble(FloatRegister lhs,
   2445                                            FloatRegister rhs) {
   2446  // Compare the doubles, setting vector status flags.
   2447  if (rhs.isMissing()) {
   2448    ma_vcmpz(lhs);
   2449  } else {
   2450    ma_vcmp(lhs, rhs);
   2451  }
   2452 
   2453  // Move vector status bits to normal status flags.
   2454  as_vmrs(pc);
   2455 }
   2456 
   2457 void MacroAssemblerARMCompat::compareFloat(FloatRegister lhs,
   2458                                           FloatRegister rhs) {
   2459  // Compare the doubles, setting vector status flags.
   2460  if (rhs.isMissing()) {
   2461    as_vcmpz(VFPRegister(lhs).singleOverlay());
   2462  } else {
   2463    as_vcmp(VFPRegister(lhs).singleOverlay(), VFPRegister(rhs).singleOverlay());
   2464  }
   2465 
   2466  // Move vector status bits to normal status flags.
   2467  as_vmrs(pc);
   2468 }
   2469 
   2470 Assembler::Condition MacroAssemblerARMCompat::testInt32(
   2471    Assembler::Condition cond, const ValueOperand& value) {
   2472  MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
   2473  ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_INT32));
   2474  return cond;
   2475 }
   2476 
   2477 Assembler::Condition MacroAssemblerARMCompat::testBoolean(
   2478    Assembler::Condition cond, const ValueOperand& value) {
   2479  MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
   2480  ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_BOOLEAN));
   2481  return cond;
   2482 }
   2483 
   2484 Assembler::Condition MacroAssemblerARMCompat::testDouble(
   2485    Assembler::Condition cond, const ValueOperand& value) {
   2486  MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
   2487  Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual;
   2488  ScratchRegisterScope scratch(asMasm());
   2489  ma_cmp(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR), scratch);
   2490  return actual;
   2491 }
   2492 
   2493 Assembler::Condition MacroAssemblerARMCompat::testNull(
   2494    Assembler::Condition cond, const ValueOperand& value) {
   2495  MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
   2496  ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_NULL));
   2497  return cond;
   2498 }
   2499 
   2500 Assembler::Condition MacroAssemblerARMCompat::testUndefined(
   2501    Assembler::Condition cond, const ValueOperand& value) {
   2502  MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
   2503  ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_UNDEFINED));
   2504  return cond;
   2505 }
   2506 
   2507 Assembler::Condition MacroAssemblerARMCompat::testString(
   2508    Assembler::Condition cond, const ValueOperand& value) {
   2509  return testString(cond, value.typeReg());
   2510 }
   2511 
   2512 Assembler::Condition MacroAssemblerARMCompat::testSymbol(
   2513    Assembler::Condition cond, const ValueOperand& value) {
   2514  return testSymbol(cond, value.typeReg());
   2515 }
   2516 
   2517 Assembler::Condition MacroAssemblerARMCompat::testBigInt(
   2518    Assembler::Condition cond, const ValueOperand& value) {
   2519  return testBigInt(cond, value.typeReg());
   2520 }
   2521 
   2522 Assembler::Condition MacroAssemblerARMCompat::testObject(
   2523    Assembler::Condition cond, const ValueOperand& value) {
   2524  return testObject(cond, value.typeReg());
   2525 }
   2526 
   2527 Assembler::Condition MacroAssemblerARMCompat::testNumber(
   2528    Assembler::Condition cond, const ValueOperand& value) {
   2529  return testNumber(cond, value.typeReg());
   2530 }
   2531 
   2532 Assembler::Condition MacroAssemblerARMCompat::testMagic(
   2533    Assembler::Condition cond, const ValueOperand& value) {
   2534  return testMagic(cond, value.typeReg());
   2535 }
   2536 
   2537 Assembler::Condition MacroAssemblerARMCompat::testPrimitive(
   2538    Assembler::Condition cond, const ValueOperand& value) {
   2539  return testPrimitive(cond, value.typeReg());
   2540 }
   2541 
   2542 Assembler::Condition MacroAssemblerARMCompat::testGCThing(
   2543    Assembler::Condition cond, const ValueOperand& value) {
   2544  return testGCThing(cond, value.typeReg());
   2545 }
   2546 
   2547 // Register-based tests.
   2548 Assembler::Condition MacroAssemblerARMCompat::testInt32(
   2549    Assembler::Condition cond, Register tag) {
   2550  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2551  ma_cmp(tag, ImmTag(JSVAL_TAG_INT32));
   2552  return cond;
   2553 }
   2554 
   2555 Assembler::Condition MacroAssemblerARMCompat::testBoolean(
   2556    Assembler::Condition cond, Register tag) {
   2557  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2558  ma_cmp(tag, ImmTag(JSVAL_TAG_BOOLEAN));
   2559  return cond;
   2560 }
   2561 
   2562 Assembler::Condition MacroAssemblerARMCompat::testNull(
   2563    Assembler::Condition cond, Register tag) {
   2564  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2565  ma_cmp(tag, ImmTag(JSVAL_TAG_NULL));
   2566  return cond;
   2567 }
   2568 
   2569 Assembler::Condition MacroAssemblerARMCompat::testUndefined(
   2570    Assembler::Condition cond, Register tag) {
   2571  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2572  ma_cmp(tag, ImmTag(JSVAL_TAG_UNDEFINED));
   2573  return cond;
   2574 }
   2575 
   2576 Assembler::Condition MacroAssemblerARMCompat::testString(
   2577    Assembler::Condition cond, Register tag) {
   2578  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2579  ma_cmp(tag, ImmTag(JSVAL_TAG_STRING));
   2580  return cond;
   2581 }
   2582 
   2583 Assembler::Condition MacroAssemblerARMCompat::testSymbol(
   2584    Assembler::Condition cond, Register tag) {
   2585  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2586  ma_cmp(tag, ImmTag(JSVAL_TAG_SYMBOL));
   2587  return cond;
   2588 }
   2589 
   2590 Assembler::Condition MacroAssemblerARMCompat::testBigInt(
   2591    Assembler::Condition cond, Register tag) {
   2592  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2593  ma_cmp(tag, ImmTag(JSVAL_TAG_BIGINT));
   2594  return cond;
   2595 }
   2596 
   2597 Assembler::Condition MacroAssemblerARMCompat::testObject(
   2598    Assembler::Condition cond, Register tag) {
   2599  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2600  ma_cmp(tag, ImmTag(JSVAL_TAG_OBJECT));
   2601  return cond;
   2602 }
   2603 
   2604 Assembler::Condition MacroAssemblerARMCompat::testMagic(
   2605    Assembler::Condition cond, Register tag) {
   2606  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2607  ma_cmp(tag, ImmTag(JSVAL_TAG_MAGIC));
   2608  return cond;
   2609 }
   2610 
   2611 Assembler::Condition MacroAssemblerARMCompat::testPrimitive(
   2612    Assembler::Condition cond, Register tag) {
   2613  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2614  ma_cmp(tag, ImmTag(JS::detail::ValueUpperExclPrimitiveTag));
   2615  return cond == Equal ? Below : AboveOrEqual;
   2616 }
   2617 
   2618 Assembler::Condition MacroAssemblerARMCompat::testGCThing(
   2619    Assembler::Condition cond, Register tag) {
   2620  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2621  ma_cmp(tag, ImmTag(JS::detail::ValueLowerInclGCThingTag));
   2622  return cond == Equal ? AboveOrEqual : Below;
   2623 }
   2624 
   2625 Assembler::Condition MacroAssemblerARMCompat::testGCThing(
   2626    Assembler::Condition cond, const Address& address) {
   2627  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2628  ScratchRegisterScope scratch(asMasm());
   2629  Register tag = extractTag(address, scratch);
   2630  ma_cmp(tag, ImmTag(JS::detail::ValueLowerInclGCThingTag));
   2631  return cond == Equal ? AboveOrEqual : Below;
   2632 }
   2633 
   2634 Assembler::Condition MacroAssemblerARMCompat::testMagic(
   2635    Assembler::Condition cond, const Address& address) {
   2636  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2637  ScratchRegisterScope scratch(asMasm());
   2638  Register tag = extractTag(address, scratch);
   2639  ma_cmp(tag, ImmTag(JSVAL_TAG_MAGIC));
   2640  return cond;
   2641 }
   2642 
   2643 Assembler::Condition MacroAssemblerARMCompat::testInt32(
   2644    Assembler::Condition cond, const Address& address) {
   2645  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2646  ScratchRegisterScope scratch(asMasm());
   2647  Register tag = extractTag(address, scratch);
   2648  ma_cmp(tag, ImmTag(JSVAL_TAG_INT32));
   2649  return cond;
   2650 }
   2651 
   2652 Assembler::Condition MacroAssemblerARMCompat::testDouble(
   2653    Condition cond, const Address& address) {
   2654  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2655  ScratchRegisterScope scratch(asMasm());
   2656  Register tag = extractTag(address, scratch);
   2657  return testDouble(cond, tag);
   2658 }
   2659 
   2660 Assembler::Condition MacroAssemblerARMCompat::testBoolean(
   2661    Condition cond, const Address& address) {
   2662  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2663  ScratchRegisterScope scratch(asMasm());
   2664  Register tag = extractTag(address, scratch);
   2665  return testBoolean(cond, tag);
   2666 }
   2667 
   2668 Assembler::Condition MacroAssemblerARMCompat::testNull(Condition cond,
   2669                                                       const Address& address) {
   2670  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2671  ScratchRegisterScope scratch(asMasm());
   2672  Register tag = extractTag(address, scratch);
   2673  return testNull(cond, tag);
   2674 }
   2675 
   2676 Assembler::Condition MacroAssemblerARMCompat::testUndefined(
   2677    Condition cond, const Address& address) {
   2678  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2679  ScratchRegisterScope scratch(asMasm());
   2680  Register tag = extractTag(address, scratch);
   2681  return testUndefined(cond, tag);
   2682 }
   2683 
   2684 Assembler::Condition MacroAssemblerARMCompat::testString(
   2685    Condition cond, const Address& address) {
   2686  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2687  ScratchRegisterScope scratch(asMasm());
   2688  Register tag = extractTag(address, scratch);
   2689  return testString(cond, tag);
   2690 }
   2691 
   2692 Assembler::Condition MacroAssemblerARMCompat::testSymbol(
   2693    Condition cond, const Address& address) {
   2694  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2695  ScratchRegisterScope scratch(asMasm());
   2696  Register tag = extractTag(address, scratch);
   2697  return testSymbol(cond, tag);
   2698 }
   2699 
   2700 Assembler::Condition MacroAssemblerARMCompat::testBigInt(
   2701    Condition cond, const Address& address) {
   2702  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2703  ScratchRegisterScope scratch(asMasm());
   2704  Register tag = extractTag(address, scratch);
   2705  return testBigInt(cond, tag);
   2706 }
   2707 
   2708 Assembler::Condition MacroAssemblerARMCompat::testObject(
   2709    Condition cond, const Address& address) {
   2710  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2711  ScratchRegisterScope scratch(asMasm());
   2712  Register tag = extractTag(address, scratch);
   2713  return testObject(cond, tag);
   2714 }
   2715 
   2716 Assembler::Condition MacroAssemblerARMCompat::testNumber(
   2717    Condition cond, const Address& address) {
   2718  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2719  ScratchRegisterScope scratch(asMasm());
   2720  Register tag = extractTag(address, scratch);
   2721  return testNumber(cond, tag);
   2722 }
   2723 
   2724 Assembler::Condition MacroAssemblerARMCompat::testDouble(Condition cond,
   2725                                                         Register tag) {
   2726  MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
   2727  Condition actual = (cond == Equal) ? Below : AboveOrEqual;
   2728  ma_cmp(tag, ImmTag(JSVAL_TAG_CLEAR));
   2729  return actual;
   2730 }
   2731 
   2732 Assembler::Condition MacroAssemblerARMCompat::testNumber(Condition cond,
   2733                                                         Register tag) {
   2734  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2735  ma_cmp(tag, ImmTag(JS::detail::ValueUpperInclNumberTag));
   2736  return cond == Equal ? BelowOrEqual : Above;
   2737 }
   2738 
   2739 Assembler::Condition MacroAssemblerARMCompat::testUndefined(
   2740    Condition cond, const BaseIndex& src) {
   2741  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2742  ScratchRegisterScope scratch(asMasm());
   2743  Register tag = extractTag(src, scratch);
   2744  ma_cmp(tag, ImmTag(JSVAL_TAG_UNDEFINED));
   2745  return cond;
   2746 }
   2747 
   2748 Assembler::Condition MacroAssemblerARMCompat::testNull(Condition cond,
   2749                                                       const BaseIndex& src) {
   2750  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2751  ScratchRegisterScope scratch(asMasm());
   2752  Register tag = extractTag(src, scratch);
   2753  ma_cmp(tag, ImmTag(JSVAL_TAG_NULL));
   2754  return cond;
   2755 }
   2756 
   2757 Assembler::Condition MacroAssemblerARMCompat::testBoolean(
   2758    Condition cond, const BaseIndex& src) {
   2759  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2760  ScratchRegisterScope scratch(asMasm());
   2761  Register tag = extractTag(src, scratch);
   2762  ma_cmp(tag, ImmTag(JSVAL_TAG_BOOLEAN));
   2763  return cond;
   2764 }
   2765 
   2766 Assembler::Condition MacroAssemblerARMCompat::testString(Condition cond,
   2767                                                         const BaseIndex& src) {
   2768  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2769  ScratchRegisterScope scratch(asMasm());
   2770  Register tag = extractTag(src, scratch);
   2771  ma_cmp(tag, ImmTag(JSVAL_TAG_STRING));
   2772  return cond;
   2773 }
   2774 
   2775 Assembler::Condition MacroAssemblerARMCompat::testSymbol(Condition cond,
   2776                                                         const BaseIndex& src) {
   2777  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2778  ScratchRegisterScope scratch(asMasm());
   2779  Register tag = extractTag(src, scratch);
   2780  ma_cmp(tag, ImmTag(JSVAL_TAG_SYMBOL));
   2781  return cond;
   2782 }
   2783 
   2784 Assembler::Condition MacroAssemblerARMCompat::testBigInt(Condition cond,
   2785                                                         const BaseIndex& src) {
   2786  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2787  ScratchRegisterScope scratch(asMasm());
   2788  Register tag = extractTag(src, scratch);
   2789  ma_cmp(tag, ImmTag(JSVAL_TAG_BIGINT));
   2790  return cond;
   2791 }
   2792 
   2793 Assembler::Condition MacroAssemblerARMCompat::testInt32(Condition cond,
   2794                                                        const BaseIndex& src) {
   2795  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2796  ScratchRegisterScope scratch(asMasm());
   2797  Register tag = extractTag(src, scratch);
   2798  ma_cmp(tag, ImmTag(JSVAL_TAG_INT32));
   2799  return cond;
   2800 }
   2801 
   2802 Assembler::Condition MacroAssemblerARMCompat::testObject(Condition cond,
   2803                                                         const BaseIndex& src) {
   2804  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2805  ScratchRegisterScope scratch(asMasm());
   2806  Register tag = extractTag(src, scratch);
   2807  ma_cmp(tag, ImmTag(JSVAL_TAG_OBJECT));
   2808  return cond;
   2809 }
   2810 
   2811 Assembler::Condition MacroAssemblerARMCompat::testDouble(Condition cond,
   2812                                                         const BaseIndex& src) {
   2813  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2814  Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual;
   2815  ScratchRegisterScope scratch(asMasm());
   2816  Register tag = extractTag(src, scratch);
   2817  ma_cmp(tag, ImmTag(JSVAL_TAG_CLEAR));
   2818  return actual;
   2819 }
   2820 
   2821 Assembler::Condition MacroAssemblerARMCompat::testMagic(
   2822    Condition cond, const BaseIndex& address) {
   2823  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2824  ScratchRegisterScope scratch(asMasm());
   2825  Register tag = extractTag(address, scratch);
   2826  ma_cmp(tag, ImmTag(JSVAL_TAG_MAGIC));
   2827  return cond;
   2828 }
   2829 
   2830 Assembler::Condition MacroAssemblerARMCompat::testGCThing(
   2831    Condition cond, const BaseIndex& address) {
   2832  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   2833  ScratchRegisterScope scratch(asMasm());
   2834  Register tag = extractTag(address, scratch);
   2835  ma_cmp(tag, ImmTag(JS::detail::ValueLowerInclGCThingTag));
   2836  return cond == Equal ? AboveOrEqual : Below;
   2837 }
   2838 
   2839 // Unboxing code.
   2840 void MacroAssemblerARMCompat::unboxNonDouble(const ValueOperand& operand,
   2841                                             Register dest, JSValueType type) {
   2842  auto movPayloadToDest = [&]() {
   2843    if (operand.payloadReg() != dest) {
   2844      ma_mov(operand.payloadReg(), dest, LeaveCC);
   2845    }
   2846  };
   2847  if (!JitOptions.spectreValueMasking) {
   2848    movPayloadToDest();
   2849    return;
   2850  }
   2851 
   2852  // Spectre mitigation: We zero the payload if the tag does not match the
   2853  // expected type and if this is a pointer type.
   2854  if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
   2855    movPayloadToDest();
   2856    return;
   2857  }
   2858 
   2859  // We zero the destination register and move the payload into it if
   2860  // the tag corresponds to the given type.
   2861  ma_cmp(operand.typeReg(), ImmType(type));
   2862  movPayloadToDest();
   2863  ma_mov(Imm32(0), dest, NotEqual);
   2864 }
   2865 
   2866 void MacroAssemblerARMCompat::unboxNonDouble(const Address& src, Register dest,
   2867                                             JSValueType type) {
   2868  ScratchRegisterScope scratch(asMasm());
   2869  if (!JitOptions.spectreValueMasking) {
   2870    ma_ldr(ToPayload(src), dest, scratch);
   2871    return;
   2872  }
   2873 
   2874  // Spectre mitigation: We zero the payload if the tag does not match the
   2875  // expected type and if this is a pointer type.
   2876  if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
   2877    ma_ldr(ToPayload(src), dest, scratch);
   2878    return;
   2879  }
   2880 
   2881  // We zero the destination register and move the payload into it if
   2882  // the tag corresponds to the given type.
   2883  ma_ldr(ToType(src), scratch, scratch);
   2884  ma_cmp(scratch, ImmType(type));
   2885  ma_ldr(ToPayload(src), dest, scratch, Offset, Equal);
   2886  ma_mov(Imm32(0), dest, NotEqual);
   2887 }
   2888 
   2889 void MacroAssemblerARMCompat::unboxNonDouble(const BaseIndex& src,
   2890                                             Register dest, JSValueType type) {
   2891  SecondScratchRegisterScope scratch2(asMasm());
   2892  ma_alu(src.base, lsl(src.index, src.scale), scratch2, OpAdd);
   2893  Address value(scratch2, src.offset);
   2894  unboxNonDouble(value, dest, type);
   2895 }
   2896 
   2897 void MacroAssemblerARMCompat::unboxDouble(const ValueOperand& operand,
   2898                                          FloatRegister dest) {
   2899  MOZ_ASSERT(dest.isDouble());
   2900  as_vxfer(operand.payloadReg(), operand.typeReg(), VFPRegister(dest),
   2901           CoreToFloat);
   2902 }
   2903 
   2904 void MacroAssemblerARMCompat::unboxDouble(const Address& src,
   2905                                          FloatRegister dest) {
   2906  MOZ_ASSERT(dest.isDouble());
   2907  loadDouble(src, dest);
   2908 }
   2909 
   2910 void MacroAssemblerARMCompat::unboxDouble(const BaseIndex& src,
   2911                                          FloatRegister dest) {
   2912  MOZ_ASSERT(dest.isDouble());
   2913  loadDouble(src, dest);
   2914 }
   2915 
   2916 void MacroAssemblerARMCompat::unboxValue(const ValueOperand& src,
   2917                                         AnyRegister dest, JSValueType type) {
   2918  if (dest.isFloat()) {
   2919    Label notInt32, end;
   2920    asMasm().branchTestInt32(Assembler::NotEqual, src, &notInt32);
   2921    convertInt32ToDouble(src.payloadReg(), dest.fpu());
   2922    ma_b(&end);
   2923    bind(&notInt32);
   2924    unboxDouble(src, dest.fpu());
   2925    bind(&end);
   2926  } else {
   2927    unboxNonDouble(src, dest.gpr(), type);
   2928  }
   2929 }
   2930 
   2931 void MacroAssemblerARMCompat::boxDouble(FloatRegister src,
   2932                                        const ValueOperand& dest,
   2933                                        FloatRegister) {
   2934  as_vxfer(dest.payloadReg(), dest.typeReg(), VFPRegister(src), FloatToCore);
   2935 }
   2936 
   2937 void MacroAssemblerARMCompat::boxNonDouble(JSValueType type, Register src,
   2938                                           const ValueOperand& dest) {
   2939  MOZ_ASSERT(type != JSVAL_TYPE_UNDEFINED && type != JSVAL_TYPE_NULL);
   2940  MOZ_ASSERT(dest.typeReg() != dest.payloadReg());
   2941 
   2942 #ifdef DEBUG
   2943  if (type == JSVAL_TYPE_BOOLEAN) {
   2944    Label upperBitsZeroed;
   2945    as_cmp(src, Imm8(1));
   2946    ma_b(&upperBitsZeroed, Assembler::BelowOrEqual);
   2947    breakpoint();
   2948    bind(&upperBitsZeroed);
   2949  }
   2950 #endif
   2951 
   2952  if (src != dest.payloadReg()) {
   2953    ma_mov(src, dest.payloadReg());
   2954  }
   2955  ma_mov(ImmType(type), dest.typeReg());
   2956 }
   2957 
   2958 void MacroAssemblerARMCompat::boxNonDouble(Register type, Register src,
   2959                                           const ValueOperand& dest) {
   2960  MOZ_ASSERT(type != dest.payloadReg() && src != dest.typeReg());
   2961 
   2962 #ifdef DEBUG
   2963  Label ok, isNullOrUndefined, isBoolean;
   2964 
   2965  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_NULL),
   2966                    &isNullOrUndefined);
   2967  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_UNDEFINED),
   2968                    &isNullOrUndefined);
   2969  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_BOOLEAN),
   2970                    &isBoolean);
   2971  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_INT32), &ok);
   2972  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_MAGIC), &ok);
   2973  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_STRING), &ok);
   2974  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_SYMBOL), &ok);
   2975  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_PRIVATE_GCTHING),
   2976                    &ok);
   2977  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_BIGINT), &ok);
   2978  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_OBJECT), &ok);
   2979  breakpoint();
   2980  {
   2981    bind(&isNullOrUndefined);
   2982    as_cmp(src, Imm8(0));
   2983    ma_b(&ok, Assembler::Zero);
   2984    breakpoint();
   2985  }
   2986  {
   2987    bind(&isBoolean);
   2988    as_cmp(src, Imm8(1));
   2989    ma_b(&ok, Assembler::BelowOrEqual);
   2990    breakpoint();
   2991  }
   2992  bind(&ok);
   2993 #endif
   2994 
   2995  if (src != dest.payloadReg()) {
   2996    ma_mov(src, dest.payloadReg());
   2997  }
   2998  ScratchRegisterScope scratch(asMasm());
   2999  ma_orr(Imm32(JSVAL_TAG_CLEAR), type, dest.typeReg(), scratch);
   3000 }
   3001 
   3002 void MacroAssemblerARMCompat::loadConstantFloat32(float f, FloatRegister dest) {
   3003  ma_vimm_f32(f, dest);
   3004 }
   3005 
   3006 void MacroAssemblerARMCompat::loadInt32OrDouble(const Address& src,
   3007                                                FloatRegister dest) {
   3008  Label notInt32, end;
   3009 
   3010  // If it's an int, convert to a double.
   3011  {
   3012    ScratchRegisterScope scratch(asMasm());
   3013    SecondScratchRegisterScope scratch2(asMasm());
   3014 
   3015    ma_ldr(ToType(src), scratch, scratch2);
   3016    asMasm().branchTestInt32(Assembler::NotEqual, scratch, &notInt32);
   3017    ma_ldr(ToPayload(src), scratch, scratch2);
   3018    convertInt32ToDouble(scratch, dest);
   3019    ma_b(&end);
   3020  }
   3021 
   3022  // Not an int, just load as double.
   3023  bind(&notInt32);
   3024  {
   3025    ScratchRegisterScope scratch(asMasm());
   3026    ma_vldr(src, dest, scratch);
   3027  }
   3028  bind(&end);
   3029 }
   3030 
   3031 void MacroAssemblerARMCompat::loadInt32OrDouble(Register base, Register index,
   3032                                                FloatRegister dest,
   3033                                                int32_t shift) {
   3034  Label notInt32, end;
   3035 
   3036  static_assert(NUNBOX32_PAYLOAD_OFFSET == 0);
   3037 
   3038  ScratchRegisterScope scratch(asMasm());
   3039 
   3040  // If it's an int, convert it to double.
   3041  ma_alu(base, lsl(index, shift), scratch, OpAdd);
   3042 
   3043  // Since we only have one scratch register, we need to stomp over it with
   3044  // the tag.
   3045  ma_ldr(DTRAddr(scratch, DtrOffImm(NUNBOX32_TYPE_OFFSET)), scratch);
   3046  asMasm().branchTestInt32(Assembler::NotEqual, scratch, &notInt32);
   3047 
   3048  // Implicitly requires NUNBOX32_PAYLOAD_OFFSET == 0: no offset provided
   3049  ma_ldr(DTRAddr(base, DtrRegImmShift(index, LSL, shift)), scratch);
   3050  convertInt32ToDouble(scratch, dest);
   3051  ma_b(&end);
   3052 
   3053  // Not an int, just load as double.
   3054  bind(&notInt32);
   3055  // First, recompute the offset that had been stored in the scratch register
   3056  // since the scratch register was overwritten loading in the type.
   3057  ma_alu(base, lsl(index, shift), scratch, OpAdd);
   3058  ma_vldr(VFPAddr(scratch, VFPOffImm(0)), dest);
   3059  bind(&end);
   3060 }
   3061 
   3062 void MacroAssemblerARMCompat::loadConstantDouble(double dp,
   3063                                                 FloatRegister dest) {
   3064  ma_vimm(dp, dest);
   3065 }
   3066 
   3067 // Treat the value as a boolean, and set condition codes accordingly.
   3068 Assembler::Condition MacroAssemblerARMCompat::testInt32Truthy(
   3069    bool truthy, const ValueOperand& operand) {
   3070  ma_tst(operand.payloadReg(), operand.payloadReg());
   3071  return truthy ? NonZero : Zero;
   3072 }
   3073 
   3074 Assembler::Condition MacroAssemblerARMCompat::testBooleanTruthy(
   3075    bool truthy, const ValueOperand& operand) {
   3076  ma_tst(operand.payloadReg(), operand.payloadReg());
   3077  return truthy ? NonZero : Zero;
   3078 }
   3079 
   3080 Assembler::Condition MacroAssemblerARMCompat::testDoubleTruthy(
   3081    bool truthy, FloatRegister reg) {
   3082  as_vcmpz(VFPRegister(reg));
   3083  as_vmrs(pc);
   3084  as_cmp(r0, O2Reg(r0), Overflow);
   3085  return truthy ? NonZero : Zero;
   3086 }
   3087 
   3088 Register MacroAssemblerARMCompat::extractObject(const Address& address,
   3089                                                Register scratch) {
   3090  SecondScratchRegisterScope scratch2(asMasm());
   3091  ma_ldr(ToPayload(address), scratch, scratch2);
   3092  return scratch;
   3093 }
   3094 
   3095 Register MacroAssemblerARMCompat::extractTag(const Address& address,
   3096                                             Register scratch) {
   3097  SecondScratchRegisterScope scratch2(asMasm());
   3098  ma_ldr(ToType(address), scratch, scratch2);
   3099  return scratch;
   3100 }
   3101 
   3102 Register MacroAssemblerARMCompat::extractTag(const BaseIndex& address,
   3103                                             Register scratch) {
   3104  ma_alu(address.base, lsl(address.index, address.scale), scratch, OpAdd,
   3105         LeaveCC);
   3106  return extractTag(Address(scratch, address.offset), scratch);
   3107 }
   3108 
   3109 /////////////////////////////////////////////////////////////////
   3110 // X86/X64-common (ARM too now) interface.
   3111 /////////////////////////////////////////////////////////////////
   3112 void MacroAssemblerARMCompat::storeValue(ValueOperand val, const Address& dst) {
   3113  SecondScratchRegisterScope scratch2(asMasm());
   3114  ma_str(val.payloadReg(), ToPayload(dst), scratch2);
   3115  ma_str(val.typeReg(), ToType(dst), scratch2);
   3116 }
   3117 
   3118 void MacroAssemblerARMCompat::storeValue(ValueOperand val,
   3119                                         const BaseIndex& dest) {
   3120  ScratchRegisterScope scratch(asMasm());
   3121 
   3122  if (isValueDTRDCandidate(val) && Abs(dest.offset) <= 255) {
   3123    Register tmpIdx;
   3124    if (dest.offset == 0) {
   3125      if (dest.scale == TimesOne) {
   3126        tmpIdx = dest.index;
   3127      } else {
   3128        ma_lsl(Imm32(dest.scale), dest.index, scratch);
   3129        tmpIdx = scratch;
   3130      }
   3131      ma_strd(val.payloadReg(), val.typeReg(),
   3132              EDtrAddr(dest.base, EDtrOffReg(tmpIdx)));
   3133    } else {
   3134      ma_alu(dest.base, lsl(dest.index, dest.scale), scratch, OpAdd);
   3135      ma_strd(val.payloadReg(), val.typeReg(),
   3136              EDtrAddr(scratch, EDtrOffImm(dest.offset)));
   3137    }
   3138  } else {
   3139    ma_alu(dest.base, lsl(dest.index, dest.scale), scratch, OpAdd);
   3140    storeValue(val, Address(scratch, dest.offset));
   3141  }
   3142 }
   3143 
   3144 void MacroAssemblerARMCompat::loadValue(const BaseIndex& addr,
   3145                                        ValueOperand val) {
   3146  ScratchRegisterScope scratch(asMasm());
   3147 
   3148  if (isValueDTRDCandidate(val) && Abs(addr.offset) <= 255) {
   3149    Register tmpIdx;
   3150    if (addr.offset == 0) {
   3151      if (addr.scale == TimesOne) {
   3152        // If the offset register is the same as one of the destination
   3153        // registers, LDRD's behavior is undefined. Use the scratch
   3154        // register to avoid this.
   3155        if (val.aliases(addr.index)) {
   3156          ma_mov(addr.index, scratch);
   3157          tmpIdx = scratch;
   3158        } else {
   3159          tmpIdx = addr.index;
   3160        }
   3161      } else {
   3162        ma_lsl(Imm32(addr.scale), addr.index, scratch);
   3163        tmpIdx = scratch;
   3164      }
   3165      ma_ldrd(EDtrAddr(addr.base, EDtrOffReg(tmpIdx)), val.payloadReg(),
   3166              val.typeReg());
   3167    } else {
   3168      ma_alu(addr.base, lsl(addr.index, addr.scale), scratch, OpAdd);
   3169      ma_ldrd(EDtrAddr(scratch, EDtrOffImm(addr.offset)), val.payloadReg(),
   3170              val.typeReg());
   3171    }
   3172  } else {
   3173    ma_alu(addr.base, lsl(addr.index, addr.scale), scratch, OpAdd);
   3174    loadValue(Address(scratch, addr.offset), val);
   3175  }
   3176 }
   3177 
   3178 void MacroAssemblerARMCompat::loadValue(Address src, ValueOperand val) {
   3179  // TODO: copy this code into a generic function that acts on all sequences
   3180  // of memory accesses
   3181  if (isValueDTRDCandidate(val)) {
   3182    // If the value we want is in two consecutive registers starting with an
   3183    // even register, they can be combined as a single ldrd.
   3184    int offset = src.offset;
   3185    if (offset < 256 && offset > -256) {
   3186      ma_ldrd(EDtrAddr(src.base, EDtrOffImm(src.offset)), val.payloadReg(),
   3187              val.typeReg());
   3188      return;
   3189    }
   3190  }
   3191  // If the value is lower than the type, then we may be able to use an ldm
   3192  // instruction.
   3193 
   3194  if (val.payloadReg().code() < val.typeReg().code()) {
   3195    if (src.offset <= 4 && src.offset >= -8 && (src.offset & 3) == 0) {
   3196      // Turns out each of the 4 value -8, -4, 0, 4 corresponds exactly
   3197      // with one of LDM{DB, DA, IA, IB}
   3198      DTMMode mode;
   3199      switch (src.offset) {
   3200        case -8:
   3201          mode = DB;
   3202          break;
   3203        case -4:
   3204          mode = DA;
   3205          break;
   3206        case 0:
   3207          mode = IA;
   3208          break;
   3209        case 4:
   3210          mode = IB;
   3211          break;
   3212        default:
   3213          MOZ_CRASH("Bogus Offset for LoadValue as DTM");
   3214      }
   3215      startDataTransferM(IsLoad, src.base, mode);
   3216      transferReg(val.payloadReg());
   3217      transferReg(val.typeReg());
   3218      finishDataTransfer();
   3219      return;
   3220    }
   3221  }
   3222 
   3223  loadUnalignedValue(src, val);
   3224 }
   3225 
   3226 void MacroAssemblerARMCompat::loadUnalignedValue(const Address& src,
   3227                                                 ValueOperand dest) {
   3228  Address payload = ToPayload(src);
   3229  Address type = ToType(src);
   3230 
   3231  // Ensure that loading the payload does not erase the pointer to the Value
   3232  // in memory.
   3233  if (type.base != dest.payloadReg()) {
   3234    SecondScratchRegisterScope scratch2(asMasm());
   3235    ma_ldr(payload, dest.payloadReg(), scratch2);
   3236    ma_ldr(type, dest.typeReg(), scratch2);
   3237  } else {
   3238    SecondScratchRegisterScope scratch2(asMasm());
   3239    ma_ldr(type, dest.typeReg(), scratch2);
   3240    ma_ldr(payload, dest.payloadReg(), scratch2);
   3241  }
   3242 }
   3243 
   3244 void MacroAssemblerARMCompat::pushValue(ValueOperand val) {
   3245  ma_push(val.typeReg());
   3246  ma_push(val.payloadReg());
   3247 }
   3248 
   3249 void MacroAssemblerARMCompat::pushValue(const Address& addr) {
   3250  ScratchRegisterScope scratch(asMasm());
   3251  SecondScratchRegisterScope scratch2(asMasm());
   3252 
   3253  ma_ldr(ToType(addr), scratch, scratch2);
   3254  ma_push(scratch);
   3255  ma_ldr(ToPayloadAfterStackPush(addr), scratch, scratch2);
   3256  ma_push(scratch);
   3257 }
   3258 
   3259 void MacroAssemblerARMCompat::pushValue(const BaseIndex& addr,
   3260                                        Register scratch) {
   3261  computeEffectiveAddress(addr, scratch);
   3262  pushValue(Address(scratch, 0));
   3263 }
   3264 
   3265 void MacroAssemblerARMCompat::popValue(ValueOperand val) {
   3266  ma_pop(val.payloadReg());
   3267  ma_pop(val.typeReg());
   3268 }
   3269 
   3270 void MacroAssemblerARMCompat::storePayload(const Value& val,
   3271                                           const Address& dest) {
   3272  ScratchRegisterScope scratch(asMasm());
   3273  SecondScratchRegisterScope scratch2(asMasm());
   3274 
   3275  if (val.isGCThing()) {
   3276    ma_mov(ImmGCPtr(val.toGCThing()), scratch);
   3277  } else {
   3278    ma_mov(Imm32(val.toNunboxPayload()), scratch);
   3279  }
   3280  ma_str(scratch, ToPayload(dest), scratch2);
   3281 }
   3282 
   3283 void MacroAssemblerARMCompat::storePayload(Register src, const Address& dest) {
   3284  ScratchRegisterScope scratch(asMasm());
   3285  ma_str(src, ToPayload(dest), scratch);
   3286 }
   3287 
   3288 void MacroAssemblerARMCompat::storePayload(const Value& val,
   3289                                           const BaseIndex& dest) {
   3290  unsigned shift = ScaleToShift(dest.scale);
   3291 
   3292  ScratchRegisterScope scratch(asMasm());
   3293  SecondScratchRegisterScope scratch2(asMasm());
   3294 
   3295  if (val.isGCThing()) {
   3296    ma_mov(ImmGCPtr(val.toGCThing()), scratch);
   3297  } else {
   3298    ma_mov(Imm32(val.toNunboxPayload()), scratch);
   3299  }
   3300 
   3301  // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index
   3302  // << shift + imm] cannot be encoded into a single instruction, and cannot
   3303  // be integrated into the as_dtr call.
   3304  static_assert(NUNBOX32_PAYLOAD_OFFSET == 0);
   3305 
   3306  // If an offset is used, modify the base so that a [base + index << shift]
   3307  // instruction format can be used.
   3308  if (dest.offset != 0) {
   3309    ma_add(dest.base, Imm32(dest.offset), dest.base, scratch2);
   3310  }
   3311 
   3312  as_dtr(IsStore, 32, Offset, scratch,
   3313         DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift)));
   3314 
   3315  // Restore the original value of the base, if necessary.
   3316  if (dest.offset != 0) {
   3317    ma_sub(dest.base, Imm32(dest.offset), dest.base, scratch);
   3318  }
   3319 }
   3320 
   3321 void MacroAssemblerARMCompat::storePayload(Register src,
   3322                                           const BaseIndex& dest) {
   3323  unsigned shift = ScaleToShift(dest.scale);
   3324  MOZ_ASSERT(shift < 32);
   3325 
   3326  ScratchRegisterScope scratch(asMasm());
   3327 
   3328  // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index
   3329  // << shift + imm] cannot be encoded into a single instruction, and cannot
   3330  // be integrated into the as_dtr call.
   3331  static_assert(NUNBOX32_PAYLOAD_OFFSET == 0);
   3332 
   3333  // Save/restore the base if the BaseIndex has an offset, as above.
   3334  if (dest.offset != 0) {
   3335    ma_add(dest.base, Imm32(dest.offset), dest.base, scratch);
   3336  }
   3337 
   3338  // Technically, shift > -32 can be handle by changing LSL to ASR, but should
   3339  // never come up, and this is one less code path to get wrong.
   3340  as_dtr(IsStore, 32, Offset, src,
   3341         DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift)));
   3342 
   3343  if (dest.offset != 0) {
   3344    ma_sub(dest.base, Imm32(dest.offset), dest.base, scratch);
   3345  }
   3346 }
   3347 
   3348 void MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const Address& dest) {
   3349  ScratchRegisterScope scratch(asMasm());
   3350  SecondScratchRegisterScope scratch2(asMasm());
   3351 
   3352  ma_mov(tag, scratch);
   3353  ma_str(scratch, ToType(dest), scratch2);
   3354 }
   3355 
   3356 void MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const BaseIndex& dest) {
   3357  Register base = dest.base;
   3358  Register index = dest.index;
   3359  unsigned shift = ScaleToShift(dest.scale);
   3360 
   3361  ScratchRegisterScope scratch(asMasm());
   3362  SecondScratchRegisterScope scratch2(asMasm());
   3363 
   3364  MOZ_ASSERT(base != scratch && base != scratch2);
   3365  MOZ_ASSERT(index != scratch && index != scratch2);
   3366 
   3367  ma_add(base, Imm32(dest.offset + NUNBOX32_TYPE_OFFSET), scratch2, scratch);
   3368  ma_mov(tag, scratch);
   3369  ma_str(scratch, DTRAddr(scratch2, DtrRegImmShift(index, LSL, shift)));
   3370 }
   3371 
   3372 void MacroAssemblerARM::ma_call(ImmPtr dest) {
   3373  ma_movPatchable(dest, CallReg, Always);
   3374  as_blx(CallReg);
   3375 }
   3376 
   3377 void MacroAssemblerARMCompat::breakpoint() { as_bkpt(); }
   3378 
   3379 void MacroAssemblerARMCompat::simulatorStop(const char* msg) {
   3380 #ifdef JS_SIMULATOR_ARM
   3381  MOZ_ASSERT(sizeof(char*) == 4);
   3382  writeInst(0xefffffff);
   3383  writeInst((int)msg);
   3384 #endif
   3385 }
   3386 
   3387 void MacroAssemblerARMCompat::breakpoint(Condition cc) {
   3388  ma_ldr(DTRAddr(r12, DtrRegImmShift(r12, LSL, 0, IsDown)), r12, Offset, cc);
   3389 }
   3390 
   3391 void MacroAssemblerARMCompat::checkStackAlignment() {
   3392  asMasm().assertStackAlignment(ABIStackAlignment);
   3393 }
   3394 
   3395 void MacroAssemblerARMCompat::handleFailureWithHandlerTail(
   3396    Label* profilerExitTail, Label* bailoutTail,
   3397    uint32_t* returnValueCheckOffset) {
   3398  // Reserve space for exception information.
   3399  int size = (sizeof(ResumeFromException) + 7) & ~7;
   3400 
   3401  Imm8 size8(size);
   3402  as_sub(sp, sp, size8);
   3403  ma_mov(sp, r0);
   3404 
   3405  // Call the handler.
   3406  using Fn = void (*)(ResumeFromException* rfe);
   3407  asMasm().setupUnalignedABICall(r1);
   3408  asMasm().passABIArg(r0);
   3409  asMasm().callWithABI<Fn, HandleException>(
   3410      ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
   3411 
   3412  *returnValueCheckOffset = asMasm().currentOffset();
   3413 
   3414  Label entryFrame;
   3415  Label catch_;
   3416  Label finally;
   3417  Label returnBaseline;
   3418  Label returnIon;
   3419  Label bailout;
   3420  Label wasmInterpEntry;
   3421  Label wasmCatch;
   3422 
   3423  {
   3424    ScratchRegisterScope scratch(asMasm());
   3425    ma_ldr(Address(sp, ResumeFromException::offsetOfKind()), r0, scratch);
   3426  }
   3427 
   3428  asMasm().branch32(Assembler::Equal, r0,
   3429                    Imm32(ExceptionResumeKind::EntryFrame), &entryFrame);
   3430  asMasm().branch32(Assembler::Equal, r0, Imm32(ExceptionResumeKind::Catch),
   3431                    &catch_);
   3432  asMasm().branch32(Assembler::Equal, r0, Imm32(ExceptionResumeKind::Finally),
   3433                    &finally);
   3434  asMasm().branch32(Assembler::Equal, r0,
   3435                    Imm32(ExceptionResumeKind::ForcedReturnBaseline),
   3436                    &returnBaseline);
   3437  asMasm().branch32(Assembler::Equal, r0,
   3438                    Imm32(ExceptionResumeKind::ForcedReturnIon), &returnIon);
   3439  asMasm().branch32(Assembler::Equal, r0, Imm32(ExceptionResumeKind::Bailout),
   3440                    &bailout);
   3441  asMasm().branch32(Assembler::Equal, r0,
   3442                    Imm32(ExceptionResumeKind::WasmInterpEntry),
   3443                    &wasmInterpEntry);
   3444  asMasm().branch32(Assembler::Equal, r0, Imm32(ExceptionResumeKind::WasmCatch),
   3445                    &wasmCatch);
   3446 
   3447  breakpoint();  // Invalid kind.
   3448 
   3449  // No exception handler. Load the error value, restore state and return from
   3450  // the entry frame.
   3451  bind(&entryFrame);
   3452  asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
   3453  {
   3454    ScratchRegisterScope scratch(asMasm());
   3455    ma_ldr(Address(sp, ResumeFromException::offsetOfFramePointer()), r11,
   3456           scratch);
   3457    ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp,
   3458           scratch);
   3459  }
   3460 
   3461  // We're going to be returning by the ion calling convention, which returns
   3462  // by ??? (for now, I think ldr pc, [sp]!)
   3463  as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(4)));
   3464 
   3465  // If we found a catch handler, this must be a baseline frame. Restore state
   3466  // and jump to the catch block.
   3467  bind(&catch_);
   3468  {
   3469    ScratchRegisterScope scratch(asMasm());
   3470    ma_ldr(Address(sp, ResumeFromException::offsetOfTarget()), r0, scratch);
   3471    ma_ldr(Address(sp, ResumeFromException::offsetOfFramePointer()), r11,
   3472           scratch);
   3473    ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp,
   3474           scratch);
   3475  }
   3476  jump(r0);
   3477 
   3478  // If we found a finally block, this must be a baseline frame. Push three
   3479  // values expected by the finally block: the exception, the exception stack,
   3480  // and BooleanValue(true).
   3481  bind(&finally);
   3482  ValueOperand exception = ValueOperand(r1, r2);
   3483  loadValue(Operand(sp, ResumeFromException::offsetOfException()), exception);
   3484 
   3485  ValueOperand exceptionStack = ValueOperand(r3, r4);
   3486  loadValue(Operand(sp, ResumeFromException::offsetOfExceptionStack()),
   3487            exceptionStack);
   3488 
   3489  {
   3490    ScratchRegisterScope scratch(asMasm());
   3491    ma_ldr(Address(sp, ResumeFromException::offsetOfTarget()), r0, scratch);
   3492    ma_ldr(Address(sp, ResumeFromException::offsetOfFramePointer()), r11,
   3493           scratch);
   3494    ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp,
   3495           scratch);
   3496  }
   3497 
   3498  pushValue(exception);
   3499  pushValue(exceptionStack);
   3500  pushValue(BooleanValue(true));
   3501  jump(r0);
   3502 
   3503  // Return BaselineFrame->returnValue() to the caller.
   3504  // Used in debug mode and for GeneratorReturn.
   3505  Label profilingInstrumentation;
   3506  bind(&returnBaseline);
   3507  {
   3508    ScratchRegisterScope scratch(asMasm());
   3509    ma_ldr(Address(sp, ResumeFromException::offsetOfFramePointer()), r11,
   3510           scratch);
   3511    ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp,
   3512           scratch);
   3513  }
   3514  loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()),
   3515            JSReturnOperand);
   3516  jump(&profilingInstrumentation);
   3517 
   3518  // Return the given value to the caller.
   3519  bind(&returnIon);
   3520  loadValue(Address(sp, ResumeFromException::offsetOfException()),
   3521            JSReturnOperand);
   3522  {
   3523    ScratchRegisterScope scratch(asMasm());
   3524    ma_ldr(Address(sp, ResumeFromException::offsetOfFramePointer()), r11,
   3525           scratch);
   3526    ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp,
   3527           scratch);
   3528  }
   3529 
   3530  // If profiling is enabled, then update the lastProfilingFrame to refer to
   3531  // caller frame before returning. This code is shared by ForcedReturnIon
   3532  // and ForcedReturnBaseline.
   3533  bind(&profilingInstrumentation);
   3534  {
   3535    Label skipProfilingInstrumentation;
   3536    // Test if profiler enabled.
   3537    AbsoluteAddress addressOfEnabled(
   3538        asMasm().runtime()->geckoProfiler().addressOfEnabled());
   3539    asMasm().branch32(Assembler::Equal, addressOfEnabled, Imm32(0),
   3540                      &skipProfilingInstrumentation);
   3541    jump(profilerExitTail);
   3542    bind(&skipProfilingInstrumentation);
   3543  }
   3544 
   3545  ma_mov(r11, sp);
   3546  pop(r11);
   3547  ret();
   3548 
   3549  // If we are bailing out to baseline to handle an exception, jump to the
   3550  // bailout tail stub. Load 1 (true) in ReturnReg to indicate success.
   3551  bind(&bailout);
   3552  {
   3553    ScratchRegisterScope scratch(asMasm());
   3554    ma_ldr(Address(sp, ResumeFromException::offsetOfBailoutInfo()), r2,
   3555           scratch);
   3556    ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp,
   3557           scratch);
   3558    ma_mov(Imm32(1), ReturnReg);
   3559  }
   3560  jump(bailoutTail);
   3561 
   3562  // Reset SP and FP; SP is pointing to the unwound return address to the wasm
   3563  // interpreter entry, so we can just ret().
   3564  bind(&wasmInterpEntry);
   3565  {
   3566    ScratchRegisterScope scratch(asMasm());
   3567    ma_ldr(Address(sp, ResumeFromException::offsetOfFramePointer()), r11,
   3568           scratch);
   3569    ma_ldr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp,
   3570           scratch);
   3571    ma_mov(Imm32(int32_t(wasm::InterpFailInstanceReg)), InstanceReg);
   3572  }
   3573  as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(4)));
   3574 
   3575  // Found a wasm catch handler, restore state and jump to it.
   3576  bind(&wasmCatch);
   3577  wasm::GenerateJumpToCatchHandler(asMasm(), sp, r0, r1);
   3578 }
   3579 
   3580 Assembler::Condition MacroAssemblerARMCompat::testStringTruthy(
   3581    bool truthy, const ValueOperand& value) {
   3582  Register string = value.payloadReg();
   3583  ScratchRegisterScope scratch(asMasm());
   3584  SecondScratchRegisterScope scratch2(asMasm());
   3585 
   3586  ma_dtr(IsLoad, string, Imm32(JSString::offsetOfLength()), scratch, scratch2);
   3587  as_cmp(scratch, Imm8(0));
   3588  return truthy ? Assembler::NotEqual : Assembler::Equal;
   3589 }
   3590 
   3591 Assembler::Condition MacroAssemblerARMCompat::testBigIntTruthy(
   3592    bool truthy, const ValueOperand& value) {
   3593  Register bi = value.payloadReg();
   3594  ScratchRegisterScope scratch(asMasm());
   3595  SecondScratchRegisterScope scratch2(asMasm());
   3596 
   3597  ma_dtr(IsLoad, bi, Imm32(BigInt::offsetOfDigitLength()), scratch, scratch2);
   3598  as_cmp(scratch, Imm8(0));
   3599  return truthy ? Assembler::NotEqual : Assembler::Equal;
   3600 }
   3601 
   3602 void MacroAssemblerARMCompat::floor(FloatRegister input, Register output,
   3603                                    Label* bail) {
   3604  Label handleZero;
   3605  Label handleNeg;
   3606  Label fin;
   3607 
   3608  ScratchDoubleScope scratchDouble(asMasm());
   3609 
   3610  compareDouble(input, NoVFPRegister);
   3611  ma_b(&handleZero, Assembler::Equal);
   3612  ma_b(&handleNeg, Assembler::Signed);
   3613  // NaN is always a bail condition, just bail directly.
   3614  ma_b(bail, Assembler::Overflow);
   3615 
   3616  // The argument is a positive number, truncation is the path to glory. Since
   3617  // it is known to be > 0.0, explicitly convert to a larger range, then a
   3618  // value that rounds to INT_MAX is explicitly different from an argument
   3619  // that clamps to INT_MAX.
   3620  ma_vcvt_F64_U32(input, scratchDouble.uintOverlay());
   3621  ma_vxfer(scratchDouble.uintOverlay(), output);
   3622  ma_mov(output, output, SetCC);
   3623  ma_b(bail, Signed);
   3624  ma_b(&fin);
   3625 
   3626  bind(&handleZero);
   3627  // Move the top word of the double into the output reg, if it is non-zero,
   3628  // then the original value was -0.0.
   3629  as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
   3630  as_cmp(output, Imm8(0));
   3631  ma_b(bail, NonZero);
   3632  ma_b(&fin);
   3633 
   3634  bind(&handleNeg);
   3635  // Negative case, negate, then start dancing.
   3636  ma_vneg(input, input);
   3637  ma_vcvt_F64_U32(input, scratchDouble.uintOverlay());
   3638  ma_vxfer(scratchDouble.uintOverlay(), output);
   3639  ma_vcvt_U32_F64(scratchDouble.uintOverlay(), scratchDouble);
   3640  compareDouble(scratchDouble, input);
   3641  as_add(output, output, Imm8(1), LeaveCC, NotEqual);
   3642  // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
   3643  // result will still be a negative number.
   3644  as_rsb(output, output, Imm8(0), SetCC);
   3645  // Flip the negated input back to its original value.
   3646  ma_vneg(input, input);
   3647  // If the result looks non-negative, then this value didn't actually fit
   3648  // into the int range, and special handling is required. Zero is also caught
   3649  // by this case, but floor of a negative number should never be zero.
   3650  ma_b(bail, NotSigned);
   3651 
   3652  bind(&fin);
   3653 }
   3654 
   3655 void MacroAssemblerARMCompat::floorf(FloatRegister input, Register output,
   3656                                     Label* bail) {
   3657  Label handleZero;
   3658  Label handleNeg;
   3659  Label fin;
   3660  compareFloat(input, NoVFPRegister);
   3661  ma_b(&handleZero, Assembler::Equal);
   3662  ma_b(&handleNeg, Assembler::Signed);
   3663  // NaN is always a bail condition, just bail directly.
   3664  ma_b(bail, Assembler::Overflow);
   3665 
   3666  // The argument is a positive number, truncation is the path to glory; Since
   3667  // it is known to be > 0.0, explicitly convert to a larger range, then a
   3668  // value that rounds to INT_MAX is explicitly different from an argument
   3669  // that clamps to INT_MAX.
   3670  {
   3671    ScratchFloat32Scope scratch(asMasm());
   3672    ma_vcvt_F32_U32(input, scratch.uintOverlay());
   3673    ma_vxfer(VFPRegister(scratch).uintOverlay(), output);
   3674  }
   3675  ma_mov(output, output, SetCC);
   3676  ma_b(bail, Signed);
   3677  ma_b(&fin);
   3678 
   3679  bind(&handleZero);
   3680  // Move the top word of the double into the output reg, if it is non-zero,
   3681  // then the original value was -0.0.
   3682  as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore,
   3683           Always, 0);
   3684  as_cmp(output, Imm8(0));
   3685  ma_b(bail, NonZero);
   3686  ma_b(&fin);
   3687 
   3688  bind(&handleNeg);
   3689  // Negative case, negate, then start dancing.
   3690  {
   3691    ScratchFloat32Scope scratch(asMasm());
   3692    ma_vneg_f32(input, input);
   3693    ma_vcvt_F32_U32(input, scratch.uintOverlay());
   3694    ma_vxfer(VFPRegister(scratch).uintOverlay(), output);
   3695    ma_vcvt_U32_F32(scratch.uintOverlay(), scratch);
   3696    compareFloat(scratch, input);
   3697    as_add(output, output, Imm8(1), LeaveCC, NotEqual);
   3698  }
   3699  // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
   3700  // result will still be a negative number.
   3701  as_rsb(output, output, Imm8(0), SetCC);
   3702  // Flip the negated input back to its original value.
   3703  ma_vneg_f32(input, input);
   3704  // If the result looks non-negative, then this value didn't actually fit
   3705  // into the int range, and special handling is required. Zero is also caught
   3706  // by this case, but floor of a negative number should never be zero.
   3707  ma_b(bail, NotSigned);
   3708 
   3709  bind(&fin);
   3710 }
   3711 
   3712 void MacroAssemblerARMCompat::ceil(FloatRegister input, Register output,
   3713                                   Label* bail) {
   3714  Label handleZero;
   3715  Label handlePos;
   3716  Label fin;
   3717 
   3718  compareDouble(input, NoVFPRegister);
   3719  // NaN is always a bail condition, just bail directly.
   3720  ma_b(bail, Assembler::Overflow);
   3721  ma_b(&handleZero, Assembler::Equal);
   3722  ma_b(&handlePos, Assembler::NotSigned);
   3723 
   3724  ScratchDoubleScope scratchDouble(asMasm());
   3725 
   3726  // We are in the ]-Inf; 0[ range
   3727  // If we are in the ]-1; 0[ range => bailout
   3728  loadConstantDouble(-1.0, scratchDouble);
   3729  compareDouble(input, scratchDouble);
   3730  ma_b(bail, Assembler::GreaterThan);
   3731 
   3732  // We are in the ]-Inf; -1] range: ceil(x) == -floor(-x) and floor can be
   3733  // computed with direct truncation here (x > 0).
   3734  ma_vneg(input, scratchDouble);
   3735  FloatRegister ScratchUIntReg = scratchDouble.uintOverlay();
   3736  ma_vcvt_F64_U32(scratchDouble, ScratchUIntReg);
   3737  ma_vxfer(ScratchUIntReg, output);
   3738  ma_neg(output, output, SetCC);
   3739  ma_b(bail, NotSigned);
   3740  ma_b(&fin);
   3741 
   3742  // Test for 0.0 / -0.0: if the top word of the input double is not zero,
   3743  // then it was -0 and we need to bail out.
   3744  bind(&handleZero);
   3745  as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
   3746  as_cmp(output, Imm8(0));
   3747  ma_b(bail, NonZero);
   3748  ma_b(&fin);
   3749 
   3750  // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for
   3751  // non integer values, maybe bail if overflow.
   3752  bind(&handlePos);
   3753  ma_vcvt_F64_U32(input, ScratchUIntReg);
   3754  ma_vxfer(ScratchUIntReg, output);
   3755  ma_vcvt_U32_F64(ScratchUIntReg, scratchDouble);
   3756  compareDouble(scratchDouble, input);
   3757  as_add(output, output, Imm8(1), LeaveCC, NotEqual);
   3758  // Bail out if the add overflowed or the result is non positive.
   3759  ma_mov(output, output, SetCC);
   3760  ma_b(bail, Signed);
   3761  ma_b(bail, Zero);
   3762 
   3763  bind(&fin);
   3764 }
   3765 
   3766 void MacroAssemblerARMCompat::ceilf(FloatRegister input, Register output,
   3767                                    Label* bail) {
   3768  Label handleZero;
   3769  Label handlePos;
   3770  Label fin;
   3771 
   3772  compareFloat(input, NoVFPRegister);
   3773  // NaN is always a bail condition, just bail directly.
   3774  ma_b(bail, Assembler::Overflow);
   3775  ma_b(&handleZero, Assembler::Equal);
   3776  ma_b(&handlePos, Assembler::NotSigned);
   3777 
   3778  // We are in the ]-Inf; 0[ range
   3779  // If we are in the ]-1; 0[ range => bailout
   3780  {
   3781    ScratchFloat32Scope scratch(asMasm());
   3782    loadConstantFloat32(-1.f, scratch);
   3783    compareFloat(input, scratch);
   3784    ma_b(bail, Assembler::GreaterThan);
   3785  }
   3786 
   3787  // We are in the ]-Inf; -1] range: ceil(x) == -floor(-x) and floor can be
   3788  // computed with direct truncation here (x > 0).
   3789  {
   3790    ScratchDoubleScope scratchDouble(asMasm());
   3791    FloatRegister scratchFloat = scratchDouble.asSingle();
   3792    FloatRegister scratchUInt = scratchDouble.uintOverlay();
   3793 
   3794    ma_vneg_f32(input, scratchFloat);
   3795    ma_vcvt_F32_U32(scratchFloat, scratchUInt);
   3796    ma_vxfer(scratchUInt, output);
   3797    ma_neg(output, output, SetCC);
   3798    ma_b(bail, NotSigned);
   3799    ma_b(&fin);
   3800  }
   3801 
   3802  // Test for 0.0 / -0.0: if the top word of the input double is not zero,
   3803  // then it was -0 and we need to bail out.
   3804  bind(&handleZero);
   3805  as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore,
   3806           Always, 0);
   3807  as_cmp(output, Imm8(0));
   3808  ma_b(bail, NonZero);
   3809  ma_b(&fin);
   3810 
   3811  // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for
   3812  // non integer values, maybe bail if overflow.
   3813  bind(&handlePos);
   3814  {
   3815    ScratchDoubleScope scratchDouble(asMasm());
   3816    FloatRegister scratchFloat = scratchDouble.asSingle();
   3817    FloatRegister scratchUInt = scratchDouble.uintOverlay();
   3818 
   3819    ma_vcvt_F32_U32(input, scratchUInt);
   3820    ma_vxfer(scratchUInt, output);
   3821    ma_vcvt_U32_F32(scratchUInt, scratchFloat);
   3822    compareFloat(scratchFloat, input);
   3823    as_add(output, output, Imm8(1), LeaveCC, NotEqual);
   3824 
   3825    // Bail on overflow or non-positive result.
   3826    ma_mov(output, output, SetCC);
   3827    ma_b(bail, Signed);
   3828    ma_b(bail, Zero);
   3829  }
   3830 
   3831  bind(&fin);
   3832 }
   3833 
   3834 void MacroAssemblerARMCompat::mov(CodeLabel* label, Register dest) {
   3835  BufferOffset bo =
   3836      ma_movPatchable(ImmPtr(/* placeholder */ nullptr), dest, Always);
   3837  label->patchAt()->bind(bo.getOffset());
   3838  label->setLinkMode(CodeLabel::MoveImmediate);
   3839 }
   3840 
   3841 CodeOffset MacroAssemblerARMCompat::toggledJump(Label* label) {
   3842  // Emit a B that can be toggled to a CMP. See ToggleToJmp(), ToggleToCmp().
   3843  BufferOffset b = ma_b(label, Always);
   3844  CodeOffset ret(b.getOffset());
   3845  return ret;
   3846 }
   3847 
   3848 CodeOffset MacroAssemblerARMCompat::toggledCall(JitCode* target, bool enabled) {
   3849  BufferOffset bo = nextOffset();
   3850  addPendingJump(bo, ImmPtr(target->raw()), RelocationKind::JITCODE);
   3851  ScratchRegisterScope scratch(asMasm());
   3852  ma_movPatchable(ImmPtr(target->raw()), scratch, Always);
   3853  if (enabled) {
   3854    ma_blx(scratch);
   3855  } else {
   3856    ma_nop();
   3857  }
   3858  return CodeOffset(bo.getOffset());
   3859 }
   3860 
   3861 void MacroAssemblerARMCompat::round(FloatRegister input, Register output,
   3862                                    Label* bail, FloatRegister tmp) {
   3863  Label handleZero;
   3864  Label handleNeg;
   3865  Label fin;
   3866 
   3867  ScratchDoubleScope scratchDouble(asMasm());
   3868 
   3869  // Do a compare based on the original value, then do most other things based
   3870  // on the shifted value.
   3871  ma_vcmpz(input);
   3872  // Since we already know the sign bit, flip all numbers to be positive,
   3873  // stored in tmp.
   3874  ma_vabs(input, tmp);
   3875  as_vmrs(pc);
   3876  ma_b(&handleZero, Assembler::Equal);
   3877  ma_b(&handleNeg, Assembler::Signed);
   3878  // NaN is always a bail condition, just bail directly.
   3879  ma_b(bail, Assembler::Overflow);
   3880 
   3881  // The argument is a positive number, truncation is the path to glory; Since
   3882  // it is known to be > 0.0, explicitly convert to a larger range, then a
   3883  // value that rounds to INT_MAX is explicitly different from an argument
   3884  // that clamps to INT_MAX.
   3885 
   3886  // Add the biggest number less than 0.5 (not 0.5, because adding that to
   3887  // the biggest number less than 0.5 would undesirably round up to 1), and
   3888  // store the result into tmp.
   3889  loadConstantDouble(GetBiggestNumberLessThan(0.5), scratchDouble);
   3890  ma_vadd(scratchDouble, tmp, tmp);
   3891 
   3892  ma_vcvt_F64_U32(tmp, scratchDouble.uintOverlay());
   3893  ma_vxfer(VFPRegister(scratchDouble).uintOverlay(), output);
   3894  ma_mov(output, output, SetCC);
   3895  ma_b(bail, Signed);
   3896  ma_b(&fin);
   3897 
   3898  bind(&handleZero);
   3899  // Move the top word of the double into the output reg, if it is non-zero,
   3900  // then the original value was -0.0
   3901  as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
   3902  as_cmp(output, Imm8(0));
   3903  ma_b(bail, NonZero);
   3904  ma_b(&fin);
   3905 
   3906  bind(&handleNeg);
   3907  // Negative case, negate, then start dancing. This number may be positive,
   3908  // since we added 0.5.
   3909 
   3910  // Add 0.5 to negative numbers, store the result into tmp
   3911  loadConstantDouble(0.5, scratchDouble);
   3912  ma_vadd(scratchDouble, tmp, tmp);
   3913 
   3914  ma_vcvt_F64_U32(tmp, scratchDouble.uintOverlay());
   3915  ma_vxfer(VFPRegister(scratchDouble).uintOverlay(), output);
   3916 
   3917  // -output is now a correctly rounded value, unless the original value was
   3918  // exactly halfway between two integers, at which point, it has been rounded
   3919  // away from zero, when it should be rounded towards \infty.
   3920  ma_vcvt_U32_F64(scratchDouble.uintOverlay(), scratchDouble);
   3921  compareDouble(scratchDouble, tmp);
   3922  as_sub(output, output, Imm8(1), LeaveCC, Equal);
   3923  // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
   3924  // result will still be a negative number.
   3925  as_rsb(output, output, Imm8(0), SetCC);
   3926 
   3927  // If the result looks non-negative, then this value didn't actually fit
   3928  // into the int range, and special handling is required, or it was zero,
   3929  // which means the result is actually -0.0 which also requires special
   3930  // handling.
   3931  ma_b(bail, NotSigned);
   3932 
   3933  bind(&fin);
   3934 }
   3935 
   3936 void MacroAssemblerARMCompat::roundf(FloatRegister input, Register output,
   3937                                     Label* bail, FloatRegister tmp) {
   3938  Label handleZero;
   3939  Label handleNeg;
   3940  Label fin;
   3941 
   3942  ScratchFloat32Scope scratchFloat(asMasm());
   3943 
   3944  // Do a compare based on the original value, then do most other things based
   3945  // on the shifted value.
   3946  compareFloat(input, NoVFPRegister);
   3947  ma_b(&handleZero, Assembler::Equal);
   3948  ma_b(&handleNeg, Assembler::Signed);
   3949 
   3950  // NaN is always a bail condition, just bail directly.
   3951  ma_b(bail, Assembler::Overflow);
   3952 
   3953  // The argument is a positive number, truncation is the path to glory; Since
   3954  // it is known to be > 0.0, explicitly convert to a larger range, then a
   3955  // value that rounds to INT_MAX is explicitly different from an argument
   3956  // that clamps to INT_MAX.
   3957 
   3958  // Add the biggest number less than 0.5f (not 0.5f, because adding that to
   3959  // the biggest number less than 0.5f would undesirably round up to 1), and
   3960  // store the result into tmp.
   3961  loadConstantFloat32(GetBiggestNumberLessThan(0.5f), scratchFloat);
   3962  ma_vadd_f32(scratchFloat, input, tmp);
   3963 
   3964  // Note: it doesn't matter whether x + .5 === x or not here, as it doesn't
   3965  // affect the semantics of the float to unsigned conversion (in particular,
   3966  // we are not applying any fixup after the operation).
   3967  ma_vcvt_F32_U32(tmp, scratchFloat.uintOverlay());
   3968  ma_vxfer(VFPRegister(scratchFloat).uintOverlay(), output);
   3969  ma_mov(output, output, SetCC);
   3970  ma_b(bail, Signed);
   3971  ma_b(&fin);
   3972 
   3973  bind(&handleZero);
   3974 
   3975  // Move the whole float32 into the output reg, if it is non-zero, then the
   3976  // original value was -0.0.
   3977  as_vxfer(output, InvalidReg, input, FloatToCore, Always, 0);
   3978  as_cmp(output, Imm8(0));
   3979  ma_b(bail, NonZero);
   3980  ma_b(&fin);
   3981 
   3982  bind(&handleNeg);
   3983 
   3984  // Add 0.5 to negative numbers, storing the result into tmp.
   3985  ma_vneg_f32(input, tmp);
   3986  loadConstantFloat32(0.5f, scratchFloat);
   3987  ma_vadd_f32(tmp, scratchFloat, scratchFloat);
   3988 
   3989  // Adding 0.5 to a float input has chances to yield the wrong result, if
   3990  // the input is too large. In this case, skip the -1 adjustment made below.
   3991  compareFloat(scratchFloat, tmp);
   3992 
   3993  // Negative case, negate, then start dancing. This number may be positive,
   3994  // since we added 0.5.
   3995  // /!\ The conditional jump afterwards depends on these two instructions
   3996  //     *not* setting the status flags. They need to not change after the
   3997  //     comparison above.
   3998  ma_vcvt_F32_U32(scratchFloat, tmp.uintOverlay());
   3999  ma_vxfer(VFPRegister(tmp).uintOverlay(), output);
   4000 
   4001  Label flipSign;
   4002  ma_b(&flipSign, Equal);
   4003 
   4004  // -output is now a correctly rounded value, unless the original value was
   4005  // exactly halfway between two integers, at which point, it has been rounded
   4006  // away from zero, when it should be rounded towards \infty.
   4007  ma_vcvt_U32_F32(tmp.uintOverlay(), tmp);
   4008  compareFloat(tmp, scratchFloat);
   4009  as_sub(output, output, Imm8(1), LeaveCC, Equal);
   4010 
   4011  // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
   4012  // result will still be a negative number.
   4013  bind(&flipSign);
   4014  as_rsb(output, output, Imm8(0), SetCC);
   4015 
   4016  // If the result looks non-negative, then this value didn't actually fit
   4017  // into the int range, and special handling is required, or it was zero,
   4018  // which means the result is actually -0.0 which also requires special
   4019  // handling.
   4020  ma_b(bail, NotSigned);
   4021 
   4022  bind(&fin);
   4023 }
   4024 
   4025 void MacroAssemblerARMCompat::trunc(FloatRegister input, Register output,
   4026                                    Label* bail) {
   4027  Label handleZero;
   4028  Label handlePos;
   4029  Label fin;
   4030 
   4031  compareDouble(input, NoVFPRegister);
   4032  // NaN is always a bail condition, just bail directly.
   4033  ma_b(bail, Assembler::Overflow);
   4034  ma_b(&handleZero, Assembler::Equal);
   4035  ma_b(&handlePos, Assembler::NotSigned);
   4036 
   4037  ScratchDoubleScope scratchDouble(asMasm());
   4038 
   4039  // We are in the ]-Inf; 0[ range
   4040  // If we are in the ]-1; 0[ range => bailout
   4041  loadConstantDouble(-1.0, scratchDouble);
   4042  compareDouble(input, scratchDouble);
   4043  ma_b(bail, Assembler::GreaterThan);
   4044 
   4045  // We are in the ]-Inf; -1] range: trunc(x) == -floor(-x) and floor can be
   4046  // computed with direct truncation here (x > 0).
   4047  ma_vneg(input, scratchDouble);
   4048  ma_vcvt_F64_U32(scratchDouble, scratchDouble.uintOverlay());
   4049  ma_vxfer(scratchDouble.uintOverlay(), output);
   4050  ma_neg(output, output, SetCC);
   4051  ma_b(bail, NotSigned);
   4052  ma_b(&fin);
   4053 
   4054  // Test for 0.0 / -0.0: if the top word of the input double is not zero,
   4055  // then it was -0 and we need to bail out.
   4056  bind(&handleZero);
   4057  as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
   4058  as_cmp(output, Imm8(0));
   4059  ma_b(bail, NonZero);
   4060  ma_b(&fin);
   4061 
   4062  // We are in the ]0; +inf] range: truncation is the path to glory. Since
   4063  // it is known to be > 0.0, explicitly convert to a larger range, then a
   4064  // value that rounds to INT_MAX is explicitly different from an argument
   4065  // that clamps to INT_MAX.
   4066  bind(&handlePos);
   4067  ma_vcvt_F64_U32(input, scratchDouble.uintOverlay());
   4068  ma_vxfer(scratchDouble.uintOverlay(), output);
   4069  ma_mov(output, output, SetCC);
   4070  ma_b(bail, Signed);
   4071 
   4072  bind(&fin);
   4073 }
   4074 
   4075 void MacroAssemblerARMCompat::truncf(FloatRegister input, Register output,
   4076                                     Label* bail) {
   4077  Label handleZero;
   4078  Label handlePos;
   4079  Label fin;
   4080 
   4081  compareFloat(input, NoVFPRegister);
   4082  // NaN is always a bail condition, just bail directly.
   4083  ma_b(bail, Assembler::Overflow);
   4084  ma_b(&handleZero, Assembler::Equal);
   4085  ma_b(&handlePos, Assembler::NotSigned);
   4086 
   4087  // We are in the ]-Inf; 0[ range
   4088  // If we are in the ]-1; 0[ range => bailout
   4089  {
   4090    ScratchFloat32Scope scratch(asMasm());
   4091    loadConstantFloat32(-1.f, scratch);
   4092    compareFloat(input, scratch);
   4093    ma_b(bail, Assembler::GreaterThan);
   4094  }
   4095 
   4096  // We are in the ]-Inf; -1] range: trunc(x) == -floor(-x) and floor can be
   4097  // computed with direct truncation here (x > 0).
   4098  {
   4099    ScratchDoubleScope scratchDouble(asMasm());
   4100    FloatRegister scratchFloat = scratchDouble.asSingle();
   4101    FloatRegister scratchUInt = scratchDouble.uintOverlay();
   4102 
   4103    ma_vneg_f32(input, scratchFloat);
   4104    ma_vcvt_F32_U32(scratchFloat, scratchUInt);
   4105    ma_vxfer(scratchUInt, output);
   4106    ma_neg(output, output, SetCC);
   4107    ma_b(bail, NotSigned);
   4108    ma_b(&fin);
   4109  }
   4110 
   4111  // Test for 0.0 / -0.0: if the top word of the input double is not zero,
   4112  // then it was -0 and we need to bail out.
   4113  bind(&handleZero);
   4114  as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore,
   4115           Always, 0);
   4116  as_cmp(output, Imm8(0));
   4117  ma_b(bail, NonZero);
   4118  ma_b(&fin);
   4119 
   4120  // We are in the ]0; +inf] range: truncation is the path to glory; Since
   4121  // it is known to be > 0.0, explicitly convert to a larger range, then a
   4122  // value that rounds to INT_MAX is explicitly different from an argument
   4123  bind(&handlePos);
   4124  {
   4125    // The argument is a positive number,
   4126    // that clamps to INT_MAX.
   4127    {
   4128      ScratchFloat32Scope scratch(asMasm());
   4129      ma_vcvt_F32_U32(input, scratch.uintOverlay());
   4130      ma_vxfer(VFPRegister(scratch).uintOverlay(), output);
   4131    }
   4132    ma_mov(output, output, SetCC);
   4133    ma_b(bail, Signed);
   4134  }
   4135 
   4136  bind(&fin);
   4137 }
   4138 
   4139 void MacroAssemblerARMCompat::profilerEnterFrame(Register framePtr,
   4140                                                 Register scratch) {
   4141  asMasm().loadJSContext(scratch);
   4142  loadPtr(Address(scratch, offsetof(JSContext, profilingActivation_)), scratch);
   4143  storePtr(framePtr,
   4144           Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
   4145  storePtr(ImmPtr(nullptr),
   4146           Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
   4147 }
   4148 
   4149 void MacroAssemblerARMCompat::profilerExitFrame() {
   4150  jump(asMasm().runtime()->jitRuntime()->getProfilerExitFrameTail());
   4151 }
   4152 
   4153 MacroAssembler& MacroAssemblerARM::asMasm() {
   4154  return *static_cast<MacroAssembler*>(this);
   4155 }
   4156 
   4157 const MacroAssembler& MacroAssemblerARM::asMasm() const {
   4158  return *static_cast<const MacroAssembler*>(this);
   4159 }
   4160 
   4161 MacroAssembler& MacroAssemblerARMCompat::asMasm() {
   4162  return *static_cast<MacroAssembler*>(this);
   4163 }
   4164 
   4165 const MacroAssembler& MacroAssemblerARMCompat::asMasm() const {
   4166  return *static_cast<const MacroAssembler*>(this);
   4167 }
   4168 
   4169 void MacroAssembler::subFromStackPtr(Imm32 imm32) {
   4170  ScratchRegisterScope scratch(*this);
   4171  if (imm32.value) {
   4172    ma_sub(imm32, sp, scratch);
   4173  }
   4174 }
   4175 
   4176 //{{{ check_macroassembler_style
   4177 // ===============================================================
   4178 // MacroAssembler high-level usage.
   4179 
   4180 void MacroAssembler::flush() { Assembler::flush(); }
   4181 
   4182 void MacroAssembler::comment(const char* msg) { Assembler::comment(msg); }
   4183 
   4184 // ===============================================================
   4185 // Stack manipulation functions.
   4186 
   4187 size_t MacroAssembler::PushRegsInMaskSizeInBytes(LiveRegisterSet set) {
   4188  return set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes();
   4189 }
   4190 
   4191 void MacroAssembler::PushRegsInMask(LiveRegisterSet set) {
   4192  mozilla::DebugOnly<size_t> framePushedInitial = framePushed();
   4193 
   4194  int32_t diffF = set.fpus().getPushSizeInBytes();
   4195  int32_t diffG = set.gprs().size() * sizeof(intptr_t);
   4196 
   4197  if (set.gprs().size() > 1) {
   4198    adjustFrame(diffG);
   4199    startDataTransferM(IsStore, StackPointer, DB, WriteBack);
   4200    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more();
   4201         ++iter) {
   4202      diffG -= sizeof(intptr_t);
   4203      transferReg(*iter);
   4204    }
   4205    finishDataTransfer();
   4206  } else {
   4207    reserveStack(diffG);
   4208    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more();
   4209         ++iter) {
   4210      diffG -= sizeof(intptr_t);
   4211      storePtr(*iter, Address(StackPointer, diffG));
   4212    }
   4213  }
   4214  MOZ_ASSERT(diffG == 0);
   4215 
   4216  // It's possible that the logic is just fine as it is if the reduced set
   4217  // maps SIMD pairs to plain doubles and transferMultipleByRuns() stores
   4218  // and loads doubles.
   4219 #ifdef ENABLE_WASM_SIMD
   4220 #  error "Needs more careful logic if SIMD is enabled"
   4221 #endif
   4222 
   4223  adjustFrame(diffF);
   4224  diffF += transferMultipleByRuns(set.fpus(), IsStore, StackPointer, DB);
   4225  MOZ_ASSERT(diffF == 0);
   4226 
   4227  MOZ_ASSERT(framePushed() - framePushedInitial ==
   4228             PushRegsInMaskSizeInBytes(set));
   4229 }
   4230 
   4231 void MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest,
   4232                                     Register scratch) {
   4233  mozilla::DebugOnly<size_t> offsetInitial = dest.offset;
   4234 
   4235  int32_t diffF = set.fpus().getPushSizeInBytes();
   4236  int32_t diffG = set.gprs().size() * sizeof(intptr_t);
   4237 
   4238  MOZ_ASSERT(dest.offset >= diffF + diffG);
   4239 
   4240  if (set.gprs().size() > 1) {
   4241    computeEffectiveAddress(dest, scratch);
   4242 
   4243    startDataTransferM(IsStore, scratch, DB, WriteBack);
   4244    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more();
   4245         ++iter) {
   4246      diffG -= sizeof(intptr_t);
   4247      dest.offset -= sizeof(intptr_t);
   4248      transferReg(*iter);
   4249    }
   4250    finishDataTransfer();
   4251  } else {
   4252    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more();
   4253         ++iter) {
   4254      diffG -= sizeof(intptr_t);
   4255      dest.offset -= sizeof(intptr_t);
   4256      storePtr(*iter, dest);
   4257    }
   4258  }
   4259  MOZ_ASSERT(diffG == 0);
   4260  (void)diffG;
   4261 
   4262  // See above.
   4263 #ifdef ENABLE_WASM_SIMD
   4264 #  error "Needs more careful logic if SIMD is enabled"
   4265 #endif
   4266 
   4267  MOZ_ASSERT(diffF >= 0);
   4268  if (diffF > 0) {
   4269    computeEffectiveAddress(dest, scratch);
   4270    diffF += transferMultipleByRuns(set.fpus(), IsStore, scratch, DB);
   4271  }
   4272 
   4273  MOZ_ASSERT(diffF == 0);
   4274 
   4275  // "The amount of space actually used does not exceed what
   4276  // `PushRegsInMaskSizeInBytes` claims will be used."
   4277  MOZ_ASSERT(offsetInitial - dest.offset <= PushRegsInMaskSizeInBytes(set));
   4278 }
   4279 
   4280 void MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set,
   4281                                         LiveRegisterSet ignore) {
   4282  mozilla::DebugOnly<size_t> framePushedInitial = framePushed();
   4283 
   4284  int32_t diffG = set.gprs().size() * sizeof(intptr_t);
   4285  int32_t diffF = set.fpus().getPushSizeInBytes();
   4286  const int32_t reservedG = diffG;
   4287  const int32_t reservedF = diffF;
   4288 
   4289  // See above.
   4290 #ifdef ENABLE_WASM_SIMD
   4291 #  error "Needs more careful logic if SIMD is enabled"
   4292 #endif
   4293 
   4294  // ARM can load multiple registers at once, but only if we want back all
   4295  // the registers we previously saved to the stack.
   4296  if (ignore.emptyFloat()) {
   4297    diffF -= transferMultipleByRuns(set.fpus(), IsLoad, StackPointer, IA);
   4298    adjustFrame(-reservedF);
   4299  } else {
   4300    LiveFloatRegisterSet fpset(set.fpus().reduceSetForPush());
   4301    LiveFloatRegisterSet fpignore(ignore.fpus().reduceSetForPush());
   4302    for (FloatRegisterBackwardIterator iter(fpset); iter.more(); ++iter) {
   4303      diffF -= (*iter).size();
   4304      if (!fpignore.has(*iter)) {
   4305        loadDouble(Address(StackPointer, diffF), *iter);
   4306      }
   4307    }
   4308    freeStack(reservedF);
   4309  }
   4310  MOZ_ASSERT(diffF == 0);
   4311 
   4312  if (set.gprs().size() > 1 && ignore.emptyGeneral()) {
   4313    startDataTransferM(IsLoad, StackPointer, IA, WriteBack);
   4314    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more();
   4315         ++iter) {
   4316      diffG -= sizeof(intptr_t);
   4317      transferReg(*iter);
   4318    }
   4319    finishDataTransfer();
   4320    adjustFrame(-reservedG);
   4321  } else {
   4322    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more();
   4323         ++iter) {
   4324      diffG -= sizeof(intptr_t);
   4325      if (!ignore.has(*iter)) {
   4326        loadPtr(Address(StackPointer, diffG), *iter);
   4327      }
   4328    }
   4329    freeStack(reservedG);
   4330  }
   4331  MOZ_ASSERT(diffG == 0);
   4332 
   4333  MOZ_ASSERT(framePushedInitial - framePushed() ==
   4334             PushRegsInMaskSizeInBytes(set));
   4335 }
   4336 
   4337 void MacroAssembler::Push(Register reg) {
   4338  push(reg);
   4339  adjustFrame(sizeof(intptr_t));
   4340 }
   4341 
   4342 void MacroAssembler::Push(const Imm32 imm) {
   4343  push(imm);
   4344  adjustFrame(sizeof(intptr_t));
   4345 }
   4346 
   4347 void MacroAssembler::Push(const ImmWord imm) {
   4348  push(imm);
   4349  adjustFrame(sizeof(intptr_t));
   4350 }
   4351 
   4352 void MacroAssembler::Push(const ImmPtr imm) {
   4353  Push(ImmWord(uintptr_t(imm.value)));
   4354 }
   4355 
   4356 void MacroAssembler::Push(const ImmGCPtr ptr) {
   4357  push(ptr);
   4358  adjustFrame(sizeof(intptr_t));
   4359 }
   4360 
   4361 void MacroAssembler::Push(FloatRegister reg) {
   4362  MOZ_ASSERT(reg.isFloat(), "simd128 not supported");
   4363  VFPRegister r = VFPRegister(reg);
   4364  ma_vpush(VFPRegister(reg));
   4365  adjustFrame(r.size());
   4366 }
   4367 
   4368 void MacroAssembler::PushBoxed(FloatRegister reg) {
   4369  MOZ_ASSERT(reg.isDouble());
   4370  Push(reg);
   4371 }
   4372 
   4373 void MacroAssembler::Pop(Register reg) {
   4374  ma_pop(reg);
   4375  adjustFrame(-sizeof(intptr_t));
   4376 }
   4377 
   4378 void MacroAssembler::Pop(FloatRegister reg) {
   4379  MOZ_ASSERT(reg.isFloat(), "simd128 not supported");
   4380  ma_vpop(reg);
   4381  adjustFrame(-reg.size());
   4382 }
   4383 
   4384 void MacroAssembler::Pop(const ValueOperand& val) {
   4385  popValue(val);
   4386  adjustFrame(-sizeof(Value));
   4387 }
   4388 
   4389 void MacroAssembler::PopStackPtr() {
   4390  as_dtr(IsLoad, 32, Offset, sp, DTRAddr(sp, DtrOffImm(0)));
   4391  adjustFrame(-sizeof(intptr_t));
   4392 }
   4393 
   4394 void MacroAssembler::freeStackTo(uint32_t framePushed) {
   4395  MOZ_ASSERT(framePushed <= framePushed_);
   4396  ScratchRegisterScope scratch(*this);
   4397  ma_sub(FramePointer, Imm32(int32_t(framePushed)), sp, scratch, LeaveCC,
   4398         Always);
   4399  framePushed_ = framePushed;
   4400 }
   4401 
   4402 // ===============================================================
   4403 // Simple call functions.
   4404 
   4405 CodeOffset MacroAssembler::call(Register reg) {
   4406  as_blx(reg);
   4407  return CodeOffset(currentOffset());
   4408 }
   4409 
   4410 CodeOffset MacroAssembler::call(Label* label) {
   4411  // For now, assume that it'll be nearby.
   4412  as_bl(label, Always);
   4413  return CodeOffset(currentOffset());
   4414 }
   4415 
   4416 void MacroAssembler::call(ImmWord imm) { call(ImmPtr((void*)imm.value)); }
   4417 
   4418 void MacroAssembler::call(ImmPtr imm) {
   4419  BufferOffset bo = m_buffer.nextOffset();
   4420  addPendingJump(bo, imm, RelocationKind::HARDCODED);
   4421  ma_call(imm);
   4422 }
   4423 
   4424 CodeOffset MacroAssembler::call(wasm::SymbolicAddress imm) {
   4425  movePtr(imm, CallReg);
   4426  return call(CallReg);
   4427 }
   4428 
   4429 CodeOffset MacroAssembler::call(const Address& addr) {
   4430  loadPtr(addr, CallReg);
   4431  return call(CallReg);
   4432 }
   4433 
   4434 void MacroAssembler::call(JitCode* c) {
   4435  BufferOffset bo = m_buffer.nextOffset();
   4436  addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE);
   4437  ScratchRegisterScope scratch(*this);
   4438  ma_movPatchable(ImmPtr(c->raw()), scratch, Always);
   4439  callJitNoProfiler(scratch);
   4440 }
   4441 
   4442 CodeOffset MacroAssembler::callWithPatch() {
   4443  // The caller ensures that the call is always in range using thunks (below)
   4444  // as necessary.
   4445  as_bl(BOffImm(), Always, /* documentation */ nullptr);
   4446  return CodeOffset(currentOffset());
   4447 }
   4448 
   4449 void MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset) {
   4450  BufferOffset inst(callerOffset - 4);
   4451  BOffImm off = BufferOffset(calleeOffset).diffB<BOffImm>(inst);
   4452  MOZ_RELEASE_ASSERT(!off.isInvalid(),
   4453                     "Failed to insert necessary far jump islands");
   4454  as_bl(off, Always, inst);
   4455 }
   4456 
   4457 CodeOffset MacroAssembler::farJumpWithPatch() {
   4458  static_assert(32 * 1024 * 1024 - JumpImmediateRange >
   4459                    wasm::MaxFuncs * 3 * sizeof(Instruction),
   4460                "always enough space for thunks");
   4461 
   4462  // The goal of the thunk is to be able to jump to any address without the
   4463  // usual 32MiB branch range limitation. Additionally, to make the thunk
   4464  // simple to use, the thunk does not use the constant pool or require
   4465  // patching an absolute address. Instead, a relative offset is used which
   4466  // can be patched during compilation.
   4467 
   4468  // Inhibit pools since these three words must be contiguous so that the offset
   4469  // calculations below are valid.
   4470  AutoForbidPoolsAndNops afp(this, 3);
   4471 
   4472  // When pc is used, the read value is the address of the instruction + 8.
   4473  // This is exactly the address of the uint32 word we want to load.
   4474  ScratchRegisterScope scratch(*this);
   4475  ma_ldr(DTRAddr(pc, DtrOffImm(0)), scratch);
   4476 
   4477  // Branch by making pc the destination register.
   4478  ma_add(pc, scratch, pc, LeaveCC, Always);
   4479 
   4480  // Allocate space which will be patched by patchFarJump().
   4481  CodeOffset farJump(currentOffset());
   4482  writeInst(UINT32_MAX);
   4483 
   4484  return farJump;
   4485 }
   4486 
   4487 void MacroAssembler::patchFarJump(CodeOffset farJump, uint32_t targetOffset) {
   4488  uint32_t* u32 =
   4489      reinterpret_cast<uint32_t*>(editSrc(BufferOffset(farJump.offset())));
   4490  MOZ_ASSERT(*u32 == UINT32_MAX);
   4491 
   4492  uint32_t addOffset = farJump.offset() - 4;
   4493  MOZ_ASSERT(editSrc(BufferOffset(addOffset))->is<InstALU>());
   4494 
   4495  // When pc is read as the operand of the add, its value is the address of
   4496  // the add instruction + 8.
   4497  *u32 = (targetOffset - addOffset) - 8;
   4498 }
   4499 
   4500 void MacroAssembler::patchFarJump(uint8_t* farJump, uint8_t* target) {
   4501  uint32_t* u32 = reinterpret_cast<uint32_t*>(farJump);
   4502  MOZ_ASSERT(*u32 == UINT32_MAX);
   4503 
   4504  uint8_t* addPtr = reinterpret_cast<uint8_t*>(u32) - 4;
   4505  MOZ_ASSERT(reinterpret_cast<Instruction*>(addPtr)->is<InstALU>());
   4506 
   4507  int32_t distance = target - addPtr;
   4508  MOZ_RELEASE_ASSERT(mozilla::Abs(distance) <=
   4509                     (intptr_t)jit::MaxCodeBytesPerProcess);
   4510 
   4511  // When pc is read as the operand of the add, its value is the address of
   4512  // the add instruction + 8.
   4513  *u32 = distance - 8;
   4514 }
   4515 
   4516 CodeOffset MacroAssembler::nopPatchableToCall() {
   4517  AutoForbidPoolsAndNops afp(this,
   4518                             /* max number of instructions in scope = */ 1);
   4519  ma_nop();
   4520  return CodeOffset(currentOffset());
   4521 }
   4522 
   4523 void MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target) {
   4524  uint8_t* inst = call - 4;
   4525  MOZ_ASSERT(reinterpret_cast<Instruction*>(inst)->is<InstBLImm>() ||
   4526             reinterpret_cast<Instruction*>(inst)->is<InstNOP>());
   4527 
   4528  new (inst) InstBLImm(BOffImm(target - inst), Assembler::Always);
   4529 }
   4530 
   4531 void MacroAssembler::patchCallToNop(uint8_t* call) {
   4532  uint8_t* inst = call - 4;
   4533  MOZ_ASSERT(reinterpret_cast<Instruction*>(inst)->is<InstBLImm>() ||
   4534             reinterpret_cast<Instruction*>(inst)->is<InstNOP>());
   4535  new (inst) InstNOP();
   4536 }
   4537 
   4538 CodeOffset MacroAssembler::move32WithPatch(Register dest) {
   4539  return movWithPatch(ImmWord(uintptr_t(-1)), dest);
   4540 }
   4541 
   4542 void MacroAssembler::patchMove32(CodeOffset offset, Imm32 n) {
   4543  Register dest;
   4544  Assembler::RelocStyle rs;
   4545 
   4546  {
   4547    BufferInstructionIterator iter(BufferOffset(offset.offset()), &m_buffer);
   4548    DebugOnly<const uint32_t*> val = GetPtr32Target(iter, &dest, &rs);
   4549    MOZ_ASSERT(uint32_t((const uint32_t*)val) == uint32_t(-1));
   4550  }
   4551 
   4552  // Patch over actual instructions.
   4553  {
   4554    BufferInstructionIterator iter(BufferOffset(offset.offset()), &m_buffer);
   4555    MacroAssembler::ma_mov_patch(n, dest, Always, rs, iter);
   4556  }
   4557 }
   4558 
   4559 void MacroAssembler::pushReturnAddress() { push(lr); }
   4560 
   4561 void MacroAssembler::popReturnAddress() { pop(lr); }
   4562 
   4563 // ===============================================================
   4564 // ABI function calls.
   4565 
   4566 void MacroAssembler::setupUnalignedABICall(Register scratch) {
   4567  setupNativeABICall();
   4568  dynamicAlignment_ = true;
   4569 
   4570  ma_mov(sp, scratch);
   4571  // Force sp to be aligned.
   4572  as_bic(sp, sp, Imm8(ABIStackAlignment - 1));
   4573  ma_push(scratch);
   4574 }
   4575 
   4576 void MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm) {
   4577  MOZ_ASSERT(inCall_);
   4578  uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar();
   4579 
   4580  if (dynamicAlignment_) {
   4581    // sizeof(intptr_t) accounts for the saved stack pointer pushed by
   4582    // setupUnalignedABICall.
   4583    stackForCall += ComputeByteAlignment(stackForCall + sizeof(intptr_t),
   4584                                         ABIStackAlignment);
   4585  } else {
   4586    uint32_t alignmentAtPrologue = callFromWasm ? sizeof(wasm::Frame) : 0;
   4587    stackForCall += ComputeByteAlignment(
   4588        stackForCall + framePushed() + alignmentAtPrologue, ABIStackAlignment);
   4589  }
   4590 
   4591  *stackAdjust = stackForCall;
   4592  reserveStack(stackForCall);
   4593 
   4594  // Position all arguments.
   4595  {
   4596    enoughMemory_ &= moveResolver_.resolve();
   4597    if (!enoughMemory_) {
   4598      return;
   4599    }
   4600 
   4601    MoveEmitter emitter(*this);
   4602    emitter.emit(moveResolver_);
   4603    emitter.finish();
   4604  }
   4605 
   4606  assertStackAlignment(ABIStackAlignment);
   4607 
   4608  // Save the lr register if we need to preserve it.
   4609  if (secondScratchReg_ != lr) {
   4610    ma_mov(lr, secondScratchReg_);
   4611  }
   4612 }
   4613 
   4614 void MacroAssembler::callWithABIPost(uint32_t stackAdjust, ABIType result) {
   4615  if (secondScratchReg_ != lr) {
   4616    ma_mov(secondScratchReg_, lr);
   4617  }
   4618 
   4619  if (abiArgs_.abi() == ABIKind::System && !ARMFlags::UseHardFpABI()) {
   4620    switch (result) {
   4621      case ABIType::Float64:
   4622        // Move double from r0/r1 to ReturnFloatReg.
   4623        ma_vxfer(r0, r1, ReturnDoubleReg);
   4624        break;
   4625      case ABIType::Float32:
   4626        // Move float32 from r0 to ReturnFloatReg.
   4627        ma_vxfer(r0, ReturnFloat32Reg);
   4628        break;
   4629      case ABIType::General:
   4630      case ABIType::Int64:
   4631        break;
   4632      default:
   4633        MOZ_CRASH("unexpected callWithABI result");
   4634    }
   4635  }
   4636 
   4637  freeStack(stackAdjust);
   4638 
   4639  if (dynamicAlignment_) {
   4640    // While the x86 supports pop esp, on ARM that isn't well defined, so
   4641    // just do it manually.
   4642    as_dtr(IsLoad, 32, Offset, sp, DTRAddr(sp, DtrOffImm(0)));
   4643  }
   4644 
   4645 #ifdef DEBUG
   4646  MOZ_ASSERT(inCall_);
   4647  inCall_ = false;
   4648 #endif
   4649 }
   4650 
   4651 void MacroAssembler::callWithABINoProfiler(Register fun, ABIType result) {
   4652  // Load the callee in r12, as above.
   4653  ma_mov(fun, r12);
   4654  uint32_t stackAdjust;
   4655  callWithABIPre(&stackAdjust);
   4656  call(r12);
   4657  callWithABIPost(stackAdjust, result);
   4658 }
   4659 
   4660 void MacroAssembler::callWithABINoProfiler(const Address& fun, ABIType result) {
   4661  // Load the callee in r12, no instruction between the ldr and call should
   4662  // clobber it. Note that we can't use fun.base because it may be one of the
   4663  // IntArg registers clobbered before the call.
   4664  {
   4665    ScratchRegisterScope scratch(*this);
   4666    ma_ldr(fun, r12, scratch);
   4667  }
   4668  uint32_t stackAdjust;
   4669  callWithABIPre(&stackAdjust);
   4670  call(r12);
   4671  callWithABIPost(stackAdjust, result);
   4672 }
   4673 
   4674 // ===============================================================
   4675 // Jit Frames.
   4676 
   4677 uint32_t MacroAssembler::pushFakeReturnAddress(Register scratch) {
   4678  // On ARM any references to the pc, adds an additional 8 to it, which
   4679  // correspond to 2 instructions of 4 bytes.  Thus we use an additional nop
   4680  // to pad until we reach the pushed pc.
   4681  //
   4682  // Note: In practice this should not be necessary, as this fake return
   4683  // address is never used for resuming any execution. Thus theoriticaly we
   4684  // could just do a Push(pc), and ignore the nop as well as the pool.
   4685  enterNoPool(2);
   4686  DebugOnly<uint32_t> offsetBeforePush = currentOffset();
   4687  Push(pc);  // actually pushes $pc + 8.
   4688  ma_nop();
   4689  uint32_t pseudoReturnOffset = currentOffset();
   4690  leaveNoPool();
   4691 
   4692  MOZ_ASSERT_IF(!oom(), pseudoReturnOffset - offsetBeforePush == 8);
   4693  return pseudoReturnOffset;
   4694 }
   4695 
   4696 void MacroAssembler::enterFakeExitFrameForWasm(Register cxreg, Register scratch,
   4697                                               ExitFrameType type) {
   4698  enterFakeExitFrame(cxreg, scratch, type);
   4699 }
   4700 
   4701 CodeOffset MacroAssembler::sub32FromMemAndBranchIfNegativeWithPatch(
   4702    Address address, Label* label) {
   4703  ScratchRegisterScope value32(*this);
   4704  SecondScratchRegisterScope scratch(*this);
   4705  MOZ_ASSERT(scratch != address.base);
   4706  ma_ldr(address, value32, scratch);
   4707  // -128 is arbitrary, but makes `*address` count upwards, which may help
   4708  // to identify cases where the subsequent ::patch..() call was forgotten.
   4709  ma_sub(Imm32(-128), value32, scratch, SetCC);
   4710  // Points immediately after the insn to patch
   4711  CodeOffset patchPoint = CodeOffset(currentOffset());
   4712  // This assumes that ma_str does not change the condition codes.
   4713  ma_str(value32, address, scratch);
   4714  ma_b(label, Assembler::Signed);
   4715  return patchPoint;
   4716 }
   4717 
   4718 void MacroAssembler::patchSub32FromMemAndBranchIfNegative(CodeOffset offset,
   4719                                                          Imm32 imm) {
   4720  int32_t val = imm.value;
   4721  // Patching it to zero would make the insn pointless
   4722  MOZ_RELEASE_ASSERT(val >= 1 && val <= 127);
   4723  BufferInstructionIterator iter(BufferOffset(offset.offset() - 4), &m_buffer);
   4724  uint32_t* instrPtr = const_cast<uint32_t*>(iter.cur()->raw());
   4725  // 31   27   23   19 15 11
   4726  // |    |    |    |  |  |
   4727  // 1110 0010 1001 Rn Rd imm12 = ADDS Rd, Rn, #imm12 // (expected)
   4728  // 1110 0010 0101 Rn Rd imm12 = SUBS Rd, Rn, #imm12 // (replacement)
   4729  uint32_t oldInstr = *instrPtr;
   4730  // Check opcode bits and imm field are as expected
   4731  MOZ_ASSERT((oldInstr >> 20) == 0b1110'0010'1001U);
   4732  MOZ_ASSERT((oldInstr & 0xFFF) == 128);           // as created above
   4733  uint32_t newInstr = (0b1110'0010'0101U << 20) |  // opcode bits
   4734                      (oldInstr & 0x000FF000U) |   // existing register fields
   4735                      (val & 0xFFF);               // #val
   4736  *instrPtr = newInstr;
   4737 }
   4738 
   4739 // ===============================================================
   4740 // Move instructions
   4741 
   4742 void MacroAssembler::moveValue(const ValueOperand& src,
   4743                               const ValueOperand& dest) {
   4744  Register s0 = src.typeReg();
   4745  Register s1 = src.payloadReg();
   4746  Register d0 = dest.typeReg();
   4747  Register d1 = dest.payloadReg();
   4748 
   4749  // Either one or both of the source registers could be the same as a
   4750  // destination register.
   4751  if (s1 == d0) {
   4752    if (s0 == d1) {
   4753      // If both are, this is just a swap of two registers.
   4754      ScratchRegisterScope scratch(*this);
   4755      MOZ_ASSERT(d1 != scratch);
   4756      MOZ_ASSERT(d0 != scratch);
   4757      ma_mov(d1, scratch);
   4758      ma_mov(d0, d1);
   4759      ma_mov(scratch, d0);
   4760      return;
   4761    }
   4762    // If only one is, copy that source first.
   4763    std::swap(s0, s1);
   4764    std::swap(d0, d1);
   4765  }
   4766 
   4767  if (s0 != d0) {
   4768    ma_mov(s0, d0);
   4769  }
   4770  if (s1 != d1) {
   4771    ma_mov(s1, d1);
   4772  }
   4773 }
   4774 
   4775 void MacroAssembler::moveValue(const Value& src, const ValueOperand& dest) {
   4776  ma_mov(Imm32(src.toNunboxTag()), dest.typeReg());
   4777  if (src.isGCThing()) {
   4778    ma_mov(ImmGCPtr(src.toGCThing()), dest.payloadReg());
   4779  } else {
   4780    ma_mov(Imm32(src.toNunboxPayload()), dest.payloadReg());
   4781  }
   4782 }
   4783 
   4784 // ===============================================================
   4785 // Branch functions
   4786 
   4787 void MacroAssembler::loadStoreBuffer(Register ptr, Register buffer) {
   4788  ma_lsr(Imm32(gc::ChunkShift), ptr, buffer);
   4789  ma_lsl(Imm32(gc::ChunkShift), buffer, buffer);
   4790  load32(Address(buffer, gc::ChunkStoreBufferOffset), buffer);
   4791 }
   4792 
   4793 void MacroAssembler::branchPtrInNurseryChunk(Condition cond, Register ptr,
   4794                                             Register temp, Label* label) {
   4795  Maybe<SecondScratchRegisterScope> scratch2;
   4796  if (temp == Register::Invalid()) {
   4797    scratch2.emplace(*this);
   4798    temp = scratch2.ref();
   4799  }
   4800 
   4801  MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
   4802  MOZ_ASSERT(ptr != temp);
   4803 
   4804  ma_lsr(Imm32(gc::ChunkShift), ptr, temp);
   4805  ma_lsl(Imm32(gc::ChunkShift), temp, temp);
   4806  loadPtr(Address(temp, gc::ChunkStoreBufferOffset), temp);
   4807  branchPtr(InvertCondition(cond), temp, ImmWord(0), label);
   4808 }
   4809 
   4810 void MacroAssembler::branchValueIsNurseryCell(Condition cond,
   4811                                              const Address& address,
   4812                                              Register temp, Label* label) {
   4813  MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
   4814 
   4815  Label done;
   4816 
   4817  branchTestGCThing(Assembler::NotEqual, address,
   4818                    cond == Assembler::Equal ? &done : label);
   4819 
   4820  loadPtr(ToPayload(address), temp);
   4821  SecondScratchRegisterScope scratch2(*this);
   4822  branchPtrInNurseryChunk(cond, temp, scratch2, label);
   4823 
   4824  bind(&done);
   4825 }
   4826 
   4827 void MacroAssembler::branchValueIsNurseryCell(Condition cond,
   4828                                              ValueOperand value, Register temp,
   4829                                              Label* label) {
   4830  MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
   4831 
   4832  Label done;
   4833 
   4834  branchTestGCThing(Assembler::NotEqual, value,
   4835                    cond == Assembler::Equal ? &done : label);
   4836  branchPtrInNurseryChunk(cond, value.payloadReg(), temp, label);
   4837 
   4838  bind(&done);
   4839 }
   4840 
   4841 void MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs,
   4842                                     const Value& rhs, Label* label) {
   4843  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   4844  MOZ_ASSERT(!rhs.isNaN());
   4845  // If cond == NotEqual, branch when a.payload != b.payload || a.tag !=
   4846  // b.tag. If the payloads are equal, compare the tags. If the payloads are
   4847  // not equal, short circuit true (NotEqual).
   4848  //
   4849  // If cand == Equal, branch when a.payload == b.payload && a.tag == b.tag.
   4850  // If the payloads are equal, compare the tags. If the payloads are not
   4851  // equal, short circuit false (NotEqual).
   4852  ScratchRegisterScope scratch(*this);
   4853 
   4854  if (rhs.isGCThing()) {
   4855    ma_cmp(lhs.payloadReg(), ImmGCPtr(rhs.toGCThing()), scratch);
   4856  } else {
   4857    ma_cmp(lhs.payloadReg(), Imm32(rhs.toNunboxPayload()), scratch);
   4858  }
   4859  ma_cmp(lhs.typeReg(), Imm32(rhs.toNunboxTag()), scratch, Equal);
   4860  ma_b(label, cond);
   4861 }
   4862 
   4863 void MacroAssembler::branchTestNaNValue(Condition cond, const ValueOperand& val,
   4864                                        Register temp, Label* label) {
   4865  MOZ_ASSERT(cond == Equal || cond == NotEqual);
   4866  ScratchRegisterScope scratch(*this);
   4867 
   4868  // When testing for NaN, we want to ignore the sign bit.
   4869  static_assert(JS::detail::CanonicalizedNaNSignBit == 0);
   4870  const uint32_t SignBit = mozilla::FloatingPoint<double>::kSignBit >> 32;
   4871  as_bic(temp, val.typeReg(), Imm8(SignBit));
   4872 
   4873  Value expected = DoubleValue(JS::GenericNaN());
   4874  ma_cmp(val.payloadReg(), Imm32(expected.toNunboxPayload()), scratch);
   4875  ma_cmp(temp, Imm32(expected.toNunboxTag()), scratch, Equal);
   4876  ma_b(label, cond);
   4877 }
   4878 
   4879 // ========================================================================
   4880 // Memory access primitives.
   4881 template <typename T>
   4882 void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value,
   4883                                       MIRType valueType, const T& dest) {
   4884  MOZ_ASSERT(valueType < MIRType::Value);
   4885 
   4886  if (valueType == MIRType::Double) {
   4887    storeDouble(value.reg().typedReg().fpu(), dest);
   4888    return;
   4889  }
   4890 
   4891  // Store the type tag.
   4892  storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest);
   4893 
   4894  // Store the payload.
   4895  if (value.constant()) {
   4896    storePayload(value.value(), dest);
   4897  } else {
   4898    storePayload(value.reg().typedReg().gpr(), dest);
   4899  }
   4900 }
   4901 
   4902 template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value,
   4903                                                MIRType valueType,
   4904                                                const Address& dest);
   4905 template void MacroAssembler::storeUnboxedValue(
   4906    const ConstantOrRegister& value, MIRType valueType,
   4907    const BaseObjectElementIndex& dest);
   4908 
   4909 FaultingCodeOffset MacroAssembler::wasmTrapInstruction() {
   4910  return FaultingCodeOffset(as_illegal_trap().getOffset());
   4911 }
   4912 
   4913 void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index,
   4914                                       Register boundsCheckLimit,
   4915                                       Label* label) {
   4916  as_cmp(index, O2Reg(boundsCheckLimit));
   4917  as_b(label, cond);
   4918  if (JitOptions.spectreIndexMasking) {
   4919    ma_mov(boundsCheckLimit, index, LeaveCC, cond);
   4920  }
   4921 }
   4922 
   4923 void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index,
   4924                                       Address boundsCheckLimit, Label* label) {
   4925  ScratchRegisterScope scratch(*this);
   4926  // We want to do a word load from
   4927  //   [boundsCheckLimit.base, #+boundsCheckLimit.offset],
   4928  // but the offset might exceed 4095, so we can't use ma_ldr directly.
   4929  // ma_dataTransferN will handle this correctly, but needs a scratch reg as
   4930  // an address temporary for the big-offset case.  The scratch reg is also
   4931  // used in all cases for the loaded value; that's OK.
   4932  ma_dataTransferN(IsLoad, /*size=*/32, /*IsSigned=*/false,
   4933                   boundsCheckLimit.base, Imm32(boundsCheckLimit.offset),
   4934                   scratch, scratch);
   4935  as_cmp(index, O2Reg(scratch));
   4936  as_b(label, cond);
   4937  if (JitOptions.spectreIndexMasking) {
   4938    ma_mov(scratch, index, LeaveCC, cond);
   4939  }
   4940 }
   4941 
   4942 void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index,
   4943                                       Register64 boundsCheckLimit,
   4944                                       Label* label) {
   4945  Label ifFalse;
   4946  cmp32(index.high, Imm32(0));
   4947  j(Assembler::NonZero, &ifFalse);
   4948  wasmBoundsCheck32(cond, index.low, boundsCheckLimit.low, label);
   4949  bind(&ifFalse);
   4950 }
   4951 
   4952 void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index,
   4953                                       Address boundsCheckLimit, Label* label) {
   4954  Label ifFalse;
   4955  cmp32(index.high, Imm32(0));
   4956  j(Assembler::NonZero, &ifFalse);
   4957  wasmBoundsCheck32(cond, index.low, boundsCheckLimit, label);
   4958  bind(&ifFalse);
   4959 }
   4960 
   4961 void MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input,
   4962                                                Register output,
   4963                                                bool isSaturating,
   4964                                                Label* oolEntry) {
   4965  wasmTruncateToInt32(input, output, MIRType::Double, /* isUnsigned= */ true,
   4966                      isSaturating, oolEntry);
   4967 }
   4968 
   4969 void MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input,
   4970                                               Register output,
   4971                                               bool isSaturating,
   4972                                               Label* oolEntry) {
   4973  wasmTruncateToInt32(input, output, MIRType::Double, /* isUnsigned= */ false,
   4974                      isSaturating, oolEntry);
   4975 }
   4976 
   4977 void MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input,
   4978                                                 Register output,
   4979                                                 bool isSaturating,
   4980                                                 Label* oolEntry) {
   4981  wasmTruncateToInt32(input, output, MIRType::Float32, /* isUnsigned= */ true,
   4982                      isSaturating, oolEntry);
   4983 }
   4984 
   4985 void MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input,
   4986                                                Register output,
   4987                                                bool isSaturating,
   4988                                                Label* oolEntry) {
   4989  wasmTruncateToInt32(input, output, MIRType::Float32, /* isUnsigned= */ false,
   4990                      isSaturating, oolEntry);
   4991 }
   4992 
   4993 void MacroAssembler::oolWasmTruncateCheckF32ToI32(
   4994    FloatRegister input, Register output, TruncFlags flags,
   4995    const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) {
   4996  outOfLineWasmTruncateToIntCheck(input, MIRType::Float32, MIRType::Int32,
   4997                                  flags, rejoin, trapSiteDesc);
   4998 }
   4999 
   5000 void MacroAssembler::oolWasmTruncateCheckF64ToI32(
   5001    FloatRegister input, Register output, TruncFlags flags,
   5002    const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) {
   5003  outOfLineWasmTruncateToIntCheck(input, MIRType::Double, MIRType::Int32, flags,
   5004                                  rejoin, trapSiteDesc);
   5005 }
   5006 
   5007 void MacroAssembler::oolWasmTruncateCheckF32ToI64(
   5008    FloatRegister input, Register64 output, TruncFlags flags,
   5009    const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) {
   5010  outOfLineWasmTruncateToIntCheck(input, MIRType::Float32, MIRType::Int64,
   5011                                  flags, rejoin, trapSiteDesc);
   5012 }
   5013 
   5014 void MacroAssembler::oolWasmTruncateCheckF64ToI64(
   5015    FloatRegister input, Register64 output, TruncFlags flags,
   5016    const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) {
   5017  outOfLineWasmTruncateToIntCheck(input, MIRType::Double, MIRType::Int64, flags,
   5018                                  rejoin, trapSiteDesc);
   5019 }
   5020 
   5021 void MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access,
   5022                              Register memoryBase, Register ptr,
   5023                              Register ptrScratch, AnyRegister output) {
   5024  wasmLoadImpl(access, memoryBase, ptr, ptrScratch, output,
   5025               Register64::Invalid());
   5026 }
   5027 
   5028 void MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access,
   5029                                 Register memoryBase, Register ptr,
   5030                                 Register ptrScratch, Register64 output) {
   5031  MOZ_ASSERT_IF(access.isAtomic(), access.byteSize() <= 4);
   5032  wasmLoadImpl(access, memoryBase, ptr, ptrScratch, AnyRegister(), output);
   5033 }
   5034 
   5035 void MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access,
   5036                               AnyRegister value, Register memoryBase,
   5037                               Register ptr, Register ptrScratch) {
   5038  wasmStoreImpl(access, value, Register64::Invalid(), memoryBase, ptr,
   5039                ptrScratch);
   5040 }
   5041 
   5042 void MacroAssembler::wasmStoreI64(const wasm::MemoryAccessDesc& access,
   5043                                  Register64 value, Register memoryBase,
   5044                                  Register ptr, Register ptrScratch) {
   5045  MOZ_ASSERT(!access.isAtomic());
   5046  wasmStoreImpl(access, AnyRegister(), value, memoryBase, ptr, ptrScratch);
   5047 }
   5048 
   5049 // ========================================================================
   5050 // Primitive atomic operations.
   5051 
   5052 static Register ComputePointerForAtomic(MacroAssembler& masm,
   5053                                        const BaseIndex& src, Register r) {
   5054  Register base = src.base;
   5055  Register index = src.index;
   5056  uint32_t scale = Imm32::ShiftOf(src.scale).value;
   5057  int32_t offset = src.offset;
   5058 
   5059  ScratchRegisterScope scratch(masm);
   5060 
   5061  masm.as_add(r, base, lsl(index, scale));
   5062  if (offset != 0) {
   5063    masm.ma_add(r, Imm32(offset), r, scratch);
   5064  }
   5065  return r;
   5066 }
   5067 
   5068 static Register ComputePointerForAtomic(MacroAssembler& masm,
   5069                                        const Address& src, Register r) {
   5070  ScratchRegisterScope scratch(masm);
   5071  if (src.offset == 0) {
   5072    return src.base;
   5073  }
   5074  masm.ma_add(src.base, Imm32(src.offset), r, scratch);
   5075  return r;
   5076 }
   5077 
   5078 // General algorithm:
   5079 //
   5080 //     ...    ptr, <addr>         ; compute address of item
   5081 //     dmb
   5082 // L0  ldrex* output, [ptr]
   5083 //     sxt*   output, output, 0   ; sign-extend if applicable
   5084 //     *xt*   tmp, oldval, 0      ; sign-extend or zero-extend if applicable
   5085 //     cmp    output, tmp
   5086 //     bne    L1                  ; failed - values are different
   5087 //     strex* tmp, newval, [ptr]
   5088 //     cmp    tmp, 1
   5089 //     beq    L0                  ; failed - location is dirty, retry
   5090 // L1  dmb
   5091 //
   5092 // Discussion here:  http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html.
   5093 // However note that that discussion uses 'isb' as the trailing fence.
   5094 // I've not quite figured out why, and I've gone with dmb here which
   5095 // is safe.  Also see the LLVM source, which uses 'dmb ish' generally.
   5096 // (Apple's Swift CPU apparently handles ish in a non-default, faster
   5097 // way.)
   5098 
   5099 template <typename T>
   5100 static void CompareExchange(MacroAssembler& masm,
   5101                            const wasm::MemoryAccessDesc* access,
   5102                            Scalar::Type type, Synchronization sync,
   5103                            const T& mem, Register oldval, Register newval,
   5104                            Register output) {
   5105  bool signExtend = Scalar::isSignedIntType(type);
   5106  unsigned nbytes = Scalar::byteSize(type);
   5107 
   5108  MOZ_ASSERT(nbytes <= 4);
   5109 
   5110  Label again;
   5111  Label done;
   5112 
   5113  SecondScratchRegisterScope scratch2(masm);
   5114  Register ptr = ComputePointerForAtomic(masm, mem, scratch2);
   5115 
   5116  ScratchRegisterScope scratch(masm);
   5117 
   5118  // NOTE: the generated code must match the assembly code in gen_cmpxchg in
   5119  // GenerateAtomicOperations.py
   5120  masm.memoryBarrierBefore(sync);
   5121 
   5122  masm.bind(&again);
   5123 
   5124  BufferOffset firstAccess;
   5125  switch (nbytes) {
   5126    case 1:
   5127      firstAccess = masm.as_ldrexb(output, ptr);
   5128      if (signExtend) {
   5129        masm.as_sxtb(output, output, 0);
   5130        masm.as_sxtb(scratch, oldval, 0);
   5131      } else {
   5132        masm.as_uxtb(scratch, oldval, 0);
   5133      }
   5134      break;
   5135    case 2:
   5136      firstAccess = masm.as_ldrexh(output, ptr);
   5137      if (signExtend) {
   5138        masm.as_sxth(output, output, 0);
   5139        masm.as_sxth(scratch, oldval, 0);
   5140      } else {
   5141        masm.as_uxth(scratch, oldval, 0);
   5142      }
   5143      break;
   5144    case 4:
   5145      firstAccess = masm.as_ldrex(output, ptr);
   5146      break;
   5147    default:
   5148      MOZ_CRASH();
   5149  }
   5150  if (access) {
   5151    masm.append(*access, js::wasm::TrapMachineInsnForLoad(nbytes),
   5152                FaultingCodeOffset(firstAccess.getOffset()));
   5153  }
   5154 
   5155  if (nbytes < 4) {
   5156    masm.as_cmp(output, O2Reg(scratch));
   5157  } else {
   5158    masm.as_cmp(output, O2Reg(oldval));
   5159  }
   5160  masm.as_b(&done, MacroAssembler::NotEqual);
   5161  switch (nbytes) {
   5162    case 1:
   5163      masm.as_strexb(scratch, newval, ptr);
   5164      break;
   5165    case 2:
   5166      masm.as_strexh(scratch, newval, ptr);
   5167      break;
   5168    case 4:
   5169      masm.as_strex(scratch, newval, ptr);
   5170      break;
   5171    default:
   5172      MOZ_CRASH();
   5173  }
   5174  masm.as_cmp(scratch, Imm8(1));
   5175  masm.as_b(&again, MacroAssembler::Equal);
   5176  masm.bind(&done);
   5177 
   5178  masm.memoryBarrierAfter(sync);
   5179 }
   5180 
   5181 void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync,
   5182                                     const Address& address, Register oldval,
   5183                                     Register newval, Register output) {
   5184  CompareExchange(*this, nullptr, type, sync, address, oldval, newval, output);
   5185 }
   5186 
   5187 void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync,
   5188                                     const BaseIndex& address, Register oldval,
   5189                                     Register newval, Register output) {
   5190  CompareExchange(*this, nullptr, type, sync, address, oldval, newval, output);
   5191 }
   5192 
   5193 void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access,
   5194                                         const Address& mem, Register oldval,
   5195                                         Register newval, Register output) {
   5196  CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval,
   5197                  newval, output);
   5198 }
   5199 
   5200 void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access,
   5201                                         const BaseIndex& mem, Register oldval,
   5202                                         Register newval, Register output) {
   5203  CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval,
   5204                  newval, output);
   5205 }
   5206 
   5207 template <typename T>
   5208 static void AtomicExchange(MacroAssembler& masm,
   5209                           const wasm::MemoryAccessDesc* access,
   5210                           Scalar::Type type, Synchronization sync,
   5211                           const T& mem, Register value, Register output) {
   5212  bool signExtend = Scalar::isSignedIntType(type);
   5213  unsigned nbytes = Scalar::byteSize(type);
   5214 
   5215  MOZ_ASSERT(nbytes <= 4);
   5216 
   5217  // Bug 1077321: We may further optimize for ARMv8 (AArch32) here.
   5218  Label again;
   5219  Label done;
   5220 
   5221  SecondScratchRegisterScope scratch2(masm);
   5222  Register ptr = ComputePointerForAtomic(masm, mem, scratch2);
   5223 
   5224  ScratchRegisterScope scratch(masm);
   5225 
   5226  // NOTE: the generated code must match the assembly code in gen_exchange in
   5227  // GenerateAtomicOperations.py
   5228  masm.memoryBarrierBefore(sync);
   5229 
   5230  masm.bind(&again);
   5231 
   5232  BufferOffset firstAccess;
   5233  switch (nbytes) {
   5234    case 1:
   5235      firstAccess = masm.as_ldrexb(output, ptr);
   5236      if (signExtend) {
   5237        masm.as_sxtb(output, output, 0);
   5238      }
   5239      masm.as_strexb(scratch, value, ptr);
   5240      break;
   5241    case 2:
   5242      firstAccess = masm.as_ldrexh(output, ptr);
   5243      if (signExtend) {
   5244        masm.as_sxth(output, output, 0);
   5245      }
   5246      masm.as_strexh(scratch, value, ptr);
   5247      break;
   5248    case 4:
   5249      firstAccess = masm.as_ldrex(output, ptr);
   5250      masm.as_strex(scratch, value, ptr);
   5251      break;
   5252    default:
   5253      MOZ_CRASH();
   5254  }
   5255  if (access) {
   5256    masm.append(*access, js::wasm::TrapMachineInsnForLoad(nbytes),
   5257                FaultingCodeOffset(firstAccess.getOffset()));
   5258  }
   5259 
   5260  masm.as_cmp(scratch, Imm8(1));
   5261  masm.as_b(&again, MacroAssembler::Equal);
   5262  masm.bind(&done);
   5263 
   5264  masm.memoryBarrierAfter(sync);
   5265 }
   5266 
   5267 void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync,
   5268                                    const Address& address, Register value,
   5269                                    Register output) {
   5270  AtomicExchange(*this, nullptr, type, sync, address, value, output);
   5271 }
   5272 
   5273 void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync,
   5274                                    const BaseIndex& address, Register value,
   5275                                    Register output) {
   5276  AtomicExchange(*this, nullptr, type, sync, address, value, output);
   5277 }
   5278 
   5279 void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access,
   5280                                        const Address& mem, Register value,
   5281                                        Register output) {
   5282  AtomicExchange(*this, &access, access.type(), access.sync(), mem, value,
   5283                 output);
   5284 }
   5285 
   5286 void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access,
   5287                                        const BaseIndex& mem, Register value,
   5288                                        Register output) {
   5289  AtomicExchange(*this, &access, access.type(), access.sync(), mem, value,
   5290                 output);
   5291 }
   5292 
   5293 // General algorithm:
   5294 //
   5295 //     ...    ptr, <addr>         ; compute address of item
   5296 //     dmb
   5297 // L0  ldrex* output, [ptr]
   5298 //     sxt*   output, output, 0   ; sign-extend if applicable
   5299 //     OP     tmp, output, value  ; compute value to store
   5300 //     strex* tmp2, tmp, [ptr]    ; tmp2 required by strex
   5301 //     cmp    tmp2, 1
   5302 //     beq    L0                  ; failed - location is dirty, retry
   5303 //     dmb                        ; ordering barrier required
   5304 //
   5305 // Also see notes above at compareExchange re the barrier strategy.
   5306 //
   5307 // Observe that the value being operated into the memory element need
   5308 // not be sign-extended because no OP will make use of bits to the
   5309 // left of the bits indicated by the width of the element, and neither
   5310 // output nor the bits stored are affected by OP.
   5311 
   5312 template <typename T>
   5313 static void AtomicFetchOp(MacroAssembler& masm,
   5314                          const wasm::MemoryAccessDesc* access,
   5315                          Scalar::Type type, Synchronization sync, AtomicOp op,
   5316                          const Register& value, const T& mem,
   5317                          Register flagTemp, Register output) {
   5318  bool signExtend = Scalar::isSignedIntType(type);
   5319  unsigned nbytes = Scalar::byteSize(type);
   5320 
   5321  MOZ_ASSERT(nbytes <= 4);
   5322  MOZ_ASSERT(flagTemp != InvalidReg);
   5323  MOZ_ASSERT(output != value);
   5324 
   5325  Label again;
   5326 
   5327  SecondScratchRegisterScope scratch2(masm);
   5328  Register ptr = ComputePointerForAtomic(masm, mem, scratch2);
   5329 
   5330  // NOTE: the generated code must match the assembly code in gen_fetchop in
   5331  // GenerateAtomicOperations.py
   5332  masm.memoryBarrierBefore(sync);
   5333 
   5334  ScratchRegisterScope scratch(masm);
   5335 
   5336  masm.bind(&again);
   5337 
   5338  BufferOffset firstAccess;
   5339  switch (nbytes) {
   5340    case 1:
   5341      firstAccess = masm.as_ldrexb(output, ptr);
   5342      if (signExtend) {
   5343        masm.as_sxtb(output, output, 0);
   5344      }
   5345      break;
   5346    case 2:
   5347      firstAccess = masm.as_ldrexh(output, ptr);
   5348      if (signExtend) {
   5349        masm.as_sxth(output, output, 0);
   5350      }
   5351      break;
   5352    case 4:
   5353      firstAccess = masm.as_ldrex(output, ptr);
   5354      break;
   5355    default:
   5356      MOZ_CRASH();
   5357  }
   5358  if (access) {
   5359    masm.append(*access, js::wasm::TrapMachineInsnForLoad(nbytes),
   5360                FaultingCodeOffset(firstAccess.getOffset()));
   5361  }
   5362 
   5363  switch (op) {
   5364    case AtomicOp::Add:
   5365      masm.as_add(scratch, output, O2Reg(value));
   5366      break;
   5367    case AtomicOp::Sub:
   5368      masm.as_sub(scratch, output, O2Reg(value));
   5369      break;
   5370    case AtomicOp::And:
   5371      masm.as_and(scratch, output, O2Reg(value));
   5372      break;
   5373    case AtomicOp::Or:
   5374      masm.as_orr(scratch, output, O2Reg(value));
   5375      break;
   5376    case AtomicOp::Xor:
   5377      masm.as_eor(scratch, output, O2Reg(value));
   5378      break;
   5379    default:
   5380      MOZ_CRASH();
   5381  }
   5382  // Rd must differ from the two other arguments to strex.
   5383  switch (nbytes) {
   5384    case 1:
   5385      masm.as_strexb(flagTemp, scratch, ptr);
   5386      break;
   5387    case 2:
   5388      masm.as_strexh(flagTemp, scratch, ptr);
   5389      break;
   5390    case 4:
   5391      masm.as_strex(flagTemp, scratch, ptr);
   5392      break;
   5393    default:
   5394      MOZ_CRASH();
   5395  }
   5396  masm.as_cmp(flagTemp, Imm8(1));
   5397  masm.as_b(&again, MacroAssembler::Equal);
   5398 
   5399  masm.memoryBarrierAfter(sync);
   5400 }
   5401 
   5402 void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync,
   5403                                   AtomicOp op, Register value,
   5404                                   const Address& mem, Register temp,
   5405                                   Register output) {
   5406  AtomicFetchOp(*this, nullptr, type, sync, op, value, mem, temp, output);
   5407 }
   5408 
   5409 void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync,
   5410                                   AtomicOp op, Register value,
   5411                                   const BaseIndex& mem, Register temp,
   5412                                   Register output) {
   5413  AtomicFetchOp(*this, nullptr, type, sync, op, value, mem, temp, output);
   5414 }
   5415 
   5416 void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access,
   5417                                       AtomicOp op, Register value,
   5418                                       const Address& mem, Register temp,
   5419                                       Register output) {
   5420  AtomicFetchOp(*this, &access, access.type(), access.sync(), op, value, mem,
   5421                temp, output);
   5422 }
   5423 
   5424 void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access,
   5425                                       AtomicOp op, Register value,
   5426                                       const BaseIndex& mem, Register temp,
   5427                                       Register output) {
   5428  AtomicFetchOp(*this, &access, access.type(), access.sync(), op, value, mem,
   5429                temp, output);
   5430 }
   5431 
   5432 // Uses both scratch registers, one for the address and one for a temp,
   5433 // but needs two temps for strex:
   5434 //
   5435 //     ...    ptr, <addr>         ; compute address of item
   5436 //     dmb
   5437 // L0  ldrex* temp, [ptr]
   5438 //     OP     temp, temp, value   ; compute value to store
   5439 //     strex* temp2, temp, [ptr]
   5440 //     cmp    temp2, 1
   5441 //     beq    L0                  ; failed - location is dirty, retry
   5442 //     dmb                        ; ordering barrier required
   5443 
   5444 template <typename T>
   5445 static void AtomicEffectOp(MacroAssembler& masm,
   5446                           const wasm::MemoryAccessDesc* access,
   5447                           Scalar::Type type, Synchronization sync, AtomicOp op,
   5448                           const Register& value, const T& mem,
   5449                           Register flagTemp) {
   5450  unsigned nbytes = Scalar::byteSize(type);
   5451 
   5452  MOZ_ASSERT(nbytes <= 4);
   5453  MOZ_ASSERT(flagTemp != InvalidReg);
   5454 
   5455  Label again;
   5456 
   5457  SecondScratchRegisterScope scratch2(masm);
   5458  Register ptr = ComputePointerForAtomic(masm, mem, scratch2);
   5459 
   5460  masm.memoryBarrierBefore(sync);
   5461 
   5462  ScratchRegisterScope scratch(masm);
   5463 
   5464  masm.bind(&again);
   5465 
   5466  BufferOffset firstAccess;
   5467  switch (nbytes) {
   5468    case 1:
   5469      firstAccess = masm.as_ldrexb(scratch, ptr);
   5470      break;
   5471    case 2:
   5472      firstAccess = masm.as_ldrexh(scratch, ptr);
   5473      break;
   5474    case 4:
   5475      firstAccess = masm.as_ldrex(scratch, ptr);
   5476      break;
   5477    default:
   5478      MOZ_CRASH();
   5479  }
   5480  if (access) {
   5481    masm.append(*access, js::wasm::TrapMachineInsnForLoad(nbytes),
   5482                FaultingCodeOffset(firstAccess.getOffset()));
   5483  }
   5484 
   5485  switch (op) {
   5486    case AtomicOp::Add:
   5487      masm.as_add(scratch, scratch, O2Reg(value));
   5488      break;
   5489    case AtomicOp::Sub:
   5490      masm.as_sub(scratch, scratch, O2Reg(value));
   5491      break;
   5492    case AtomicOp::And:
   5493      masm.as_and(scratch, scratch, O2Reg(value));
   5494      break;
   5495    case AtomicOp::Or:
   5496      masm.as_orr(scratch, scratch, O2Reg(value));
   5497      break;
   5498    case AtomicOp::Xor:
   5499      masm.as_eor(scratch, scratch, O2Reg(value));
   5500      break;
   5501    default:
   5502      MOZ_CRASH();
   5503  }
   5504  // Rd must differ from the two other arguments to strex.
   5505  switch (nbytes) {
   5506    case 1:
   5507      masm.as_strexb(flagTemp, scratch, ptr);
   5508      break;
   5509    case 2:
   5510      masm.as_strexh(flagTemp, scratch, ptr);
   5511      break;
   5512    case 4:
   5513      masm.as_strex(flagTemp, scratch, ptr);
   5514      break;
   5515    default:
   5516      MOZ_CRASH();
   5517  }
   5518  masm.as_cmp(flagTemp, Imm8(1));
   5519  masm.as_b(&again, MacroAssembler::Equal);
   5520 
   5521  masm.memoryBarrierAfter(sync);
   5522 }
   5523 
   5524 void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access,
   5525                                        AtomicOp op, Register value,
   5526                                        const Address& mem, Register temp) {
   5527  AtomicEffectOp(*this, &access, access.type(), access.sync(), op, value, mem,
   5528                 temp);
   5529 }
   5530 
   5531 void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access,
   5532                                        AtomicOp op, Register value,
   5533                                        const BaseIndex& mem, Register temp) {
   5534  AtomicEffectOp(*this, &access, access.type(), access.sync(), op, value, mem,
   5535                 temp);
   5536 }
   5537 
   5538 template <typename T>
   5539 static void AtomicLoad64(MacroAssembler& masm,
   5540                         const wasm::MemoryAccessDesc* access,
   5541                         Synchronization sync, const T& mem,
   5542                         Register64 output) {
   5543  MOZ_ASSERT((output.low.code() & 1) == 0);
   5544  MOZ_ASSERT(output.low.code() + 1 == output.high.code());
   5545 
   5546  masm.memoryBarrierBefore(sync);
   5547 
   5548  SecondScratchRegisterScope scratch2(masm);
   5549  Register ptr = ComputePointerForAtomic(masm, mem, scratch2);
   5550 
   5551  BufferOffset load = masm.as_ldrexd(output.low, output.high, ptr);
   5552  if (access) {
   5553    masm.append(*access, js::wasm::TrapMachineInsn::Load64,
   5554                FaultingCodeOffset(load.getOffset()));
   5555  }
   5556  masm.as_clrex();
   5557 
   5558  masm.memoryBarrierAfter(sync);
   5559 }
   5560 
   5561 template <typename T>
   5562 static void WasmAtomicLoad64(MacroAssembler& masm,
   5563                             const wasm::MemoryAccessDesc& access, const T& mem,
   5564                             Register64 temp, Register64 output) {
   5565  MOZ_ASSERT(temp.low == InvalidReg && temp.high == InvalidReg);
   5566 
   5567  AtomicLoad64(masm, &access, access.sync(), mem, output);
   5568 }
   5569 
   5570 void MacroAssembler::wasmAtomicLoad64(const wasm::MemoryAccessDesc& access,
   5571                                      const Address& mem, Register64 temp,
   5572                                      Register64 output) {
   5573  WasmAtomicLoad64(*this, access, mem, temp, output);
   5574 }
   5575 
   5576 void MacroAssembler::wasmAtomicLoad64(const wasm::MemoryAccessDesc& access,
   5577                                      const BaseIndex& mem, Register64 temp,
   5578                                      Register64 output) {
   5579  WasmAtomicLoad64(*this, access, mem, temp, output);
   5580 }
   5581 
   5582 template <typename T>
   5583 static void CompareExchange64(MacroAssembler& masm,
   5584                              const wasm::MemoryAccessDesc* access,
   5585                              Synchronization sync, const T& mem,
   5586                              Register64 expect, Register64 replace,
   5587                              Register64 output) {
   5588  MOZ_ASSERT(expect != replace && replace != output && output != expect);
   5589 
   5590  MOZ_ASSERT((replace.low.code() & 1) == 0);
   5591  MOZ_ASSERT(replace.low.code() + 1 == replace.high.code());
   5592 
   5593  MOZ_ASSERT((output.low.code() & 1) == 0);
   5594  MOZ_ASSERT(output.low.code() + 1 == output.high.code());
   5595 
   5596  Label again;
   5597  Label done;
   5598 
   5599  SecondScratchRegisterScope scratch2(masm);
   5600  Register ptr = ComputePointerForAtomic(masm, mem, scratch2);
   5601 
   5602  // NOTE: the generated code must match the assembly code in gen_cmpxchg in
   5603  // GenerateAtomicOperations.py
   5604  masm.memoryBarrierBefore(sync);
   5605 
   5606  masm.bind(&again);
   5607  BufferOffset load = masm.as_ldrexd(output.low, output.high, ptr);
   5608  if (access) {
   5609    masm.append(*access, js::wasm::TrapMachineInsn::Load64,
   5610                FaultingCodeOffset(load.getOffset()));
   5611  }
   5612 
   5613  masm.as_cmp(output.low, O2Reg(expect.low));
   5614  masm.as_cmp(output.high, O2Reg(expect.high), MacroAssembler::Equal);
   5615  masm.as_b(&done, MacroAssembler::NotEqual);
   5616 
   5617  ScratchRegisterScope scratch(masm);
   5618 
   5619  // Rd (temp) must differ from the two other arguments to strex.
   5620  masm.as_strexd(scratch, replace.low, replace.high, ptr);
   5621  masm.as_cmp(scratch, Imm8(1));
   5622  masm.as_b(&again, MacroAssembler::Equal);
   5623  masm.bind(&done);
   5624 
   5625  masm.memoryBarrierAfter(sync);
   5626 }
   5627 
   5628 void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access,
   5629                                           const Address& mem,
   5630                                           Register64 expect,
   5631                                           Register64 replace,
   5632                                           Register64 output) {
   5633  CompareExchange64(*this, &access, access.sync(), mem, expect, replace,
   5634                    output);
   5635 }
   5636 
   5637 void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access,
   5638                                           const BaseIndex& mem,
   5639                                           Register64 expect,
   5640                                           Register64 replace,
   5641                                           Register64 output) {
   5642  CompareExchange64(*this, &access, access.sync(), mem, expect, replace,
   5643                    output);
   5644 }
   5645 
   5646 void MacroAssembler::compareExchange64(Synchronization sync, const Address& mem,
   5647                                       Register64 expect, Register64 replace,
   5648                                       Register64 output) {
   5649  CompareExchange64(*this, nullptr, sync, mem, expect, replace, output);
   5650 }
   5651 
   5652 void MacroAssembler::compareExchange64(Synchronization sync,
   5653                                       const BaseIndex& mem, Register64 expect,
   5654                                       Register64 replace, Register64 output) {
   5655  CompareExchange64(*this, nullptr, sync, mem, expect, replace, output);
   5656 }
   5657 
   5658 template <typename T>
   5659 static void AtomicExchange64(MacroAssembler& masm,
   5660                             const wasm::MemoryAccessDesc* access,
   5661                             Synchronization sync, const T& mem,
   5662                             Register64 value, Register64 output) {
   5663  MOZ_ASSERT(output != value);
   5664 
   5665  MOZ_ASSERT((value.low.code() & 1) == 0);
   5666  MOZ_ASSERT(value.low.code() + 1 == value.high.code());
   5667 
   5668  MOZ_ASSERT((output.low.code() & 1) == 0);
   5669  MOZ_ASSERT(output.low.code() + 1 == output.high.code());
   5670 
   5671  Label again;
   5672 
   5673  SecondScratchRegisterScope scratch2(masm);
   5674  Register ptr = ComputePointerForAtomic(masm, mem, scratch2);
   5675 
   5676  masm.memoryBarrierBefore(sync);
   5677 
   5678  masm.bind(&again);
   5679  BufferOffset load = masm.as_ldrexd(output.low, output.high, ptr);
   5680  if (access) {
   5681    masm.append(*access, js::wasm::TrapMachineInsn::Load64,
   5682                FaultingCodeOffset(load.getOffset()));
   5683  }
   5684 
   5685  ScratchRegisterScope scratch(masm);
   5686 
   5687  masm.as_strexd(scratch, value.low, value.high, ptr);
   5688  masm.as_cmp(scratch, Imm8(1));
   5689  masm.as_b(&again, MacroAssembler::Equal);
   5690 
   5691  masm.memoryBarrierAfter(sync);
   5692 }
   5693 
   5694 template <typename T>
   5695 static void WasmAtomicExchange64(MacroAssembler& masm,
   5696                                 const wasm::MemoryAccessDesc& access,
   5697                                 const T& mem, Register64 value,
   5698                                 Register64 output) {
   5699  AtomicExchange64(masm, &access, access.sync(), mem, value, output);
   5700 }
   5701 
   5702 void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access,
   5703                                          const Address& mem, Register64 value,
   5704                                          Register64 output) {
   5705  WasmAtomicExchange64(*this, access, mem, value, output);
   5706 }
   5707 
   5708 void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access,
   5709                                          const BaseIndex& mem,
   5710                                          Register64 value, Register64 output) {
   5711  WasmAtomicExchange64(*this, access, mem, value, output);
   5712 }
   5713 
   5714 void MacroAssembler::atomicExchange64(Synchronization sync, const Address& mem,
   5715                                      Register64 value, Register64 output) {
   5716  AtomicExchange64(*this, nullptr, sync, mem, value, output);
   5717 }
   5718 
   5719 void MacroAssembler::atomicExchange64(Synchronization sync,
   5720                                      const BaseIndex& mem, Register64 value,
   5721                                      Register64 output) {
   5722  AtomicExchange64(*this, nullptr, sync, mem, value, output);
   5723 }
   5724 
   5725 template <typename T>
   5726 static void AtomicFetchOp64(MacroAssembler& masm,
   5727                            const wasm::MemoryAccessDesc* access,
   5728                            Synchronization sync, AtomicOp op, Register64 value,
   5729                            const T& mem, Register64 temp, Register64 output) {
   5730  MOZ_ASSERT(temp.low != InvalidReg && temp.high != InvalidReg);
   5731  MOZ_ASSERT(output != value);
   5732  MOZ_ASSERT(temp != value);
   5733 
   5734  MOZ_ASSERT((temp.low.code() & 1) == 0);
   5735  MOZ_ASSERT(temp.low.code() + 1 == temp.high.code());
   5736 
   5737  // We could avoid this pair requirement but in that case we would end up
   5738  // with two moves in the loop to preserve the loaded value in output.  The
   5739  // prize would be less register spilling around this op since the pair
   5740  // requirement will tend to force more spilling.
   5741 
   5742  MOZ_ASSERT((output.low.code() & 1) == 0);
   5743  MOZ_ASSERT(output.low.code() + 1 == output.high.code());
   5744 
   5745  Label again;
   5746 
   5747  SecondScratchRegisterScope scratch2(masm);
   5748  Register ptr = ComputePointerForAtomic(masm, mem, scratch2);
   5749 
   5750  masm.memoryBarrierBefore(sync);
   5751 
   5752  masm.bind(&again);
   5753  BufferOffset load = masm.as_ldrexd(output.low, output.high, ptr);
   5754  if (access) {
   5755    masm.append(*access, js::wasm::TrapMachineInsn::Load64,
   5756                FaultingCodeOffset(load.getOffset()));
   5757  }
   5758  switch (op) {
   5759    case AtomicOp::Add:
   5760      masm.as_add(temp.low, output.low, O2Reg(value.low), SetCC);
   5761      masm.as_adc(temp.high, output.high, O2Reg(value.high));
   5762      break;
   5763    case AtomicOp::Sub:
   5764      masm.as_sub(temp.low, output.low, O2Reg(value.low), SetCC);
   5765      masm.as_sbc(temp.high, output.high, O2Reg(value.high));
   5766      break;
   5767    case AtomicOp::And:
   5768      masm.as_and(temp.low, output.low, O2Reg(value.low));
   5769      masm.as_and(temp.high, output.high, O2Reg(value.high));
   5770      break;
   5771    case AtomicOp::Or:
   5772      masm.as_orr(temp.low, output.low, O2Reg(value.low));
   5773      masm.as_orr(temp.high, output.high, O2Reg(value.high));
   5774      break;
   5775    case AtomicOp::Xor:
   5776      masm.as_eor(temp.low, output.low, O2Reg(value.low));
   5777      masm.as_eor(temp.high, output.high, O2Reg(value.high));
   5778      break;
   5779  }
   5780 
   5781  ScratchRegisterScope scratch(masm);
   5782 
   5783  // Rd (temp) must differ from the two other arguments to strex.
   5784  masm.as_strexd(scratch, temp.low, temp.high, ptr);
   5785  masm.as_cmp(scratch, Imm8(1));
   5786  masm.as_b(&again, MacroAssembler::Equal);
   5787 
   5788  masm.memoryBarrierAfter(sync);
   5789 }
   5790 
   5791 template <typename T>
   5792 static void WasmAtomicFetchOp64(MacroAssembler& masm,
   5793                                const wasm::MemoryAccessDesc& access,
   5794                                AtomicOp op, Register64 value, const T& mem,
   5795                                Register64 temp, Register64 output) {
   5796  AtomicFetchOp64(masm, &access, access.sync(), op, value, mem, temp, output);
   5797 }
   5798 
   5799 void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access,
   5800                                         AtomicOp op, Register64 value,
   5801                                         const Address& mem, Register64 temp,
   5802                                         Register64 output) {
   5803  WasmAtomicFetchOp64(*this, access, op, value, mem, temp, output);
   5804 }
   5805 
   5806 void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access,
   5807                                         AtomicOp op, Register64 value,
   5808                                         const BaseIndex& mem, Register64 temp,
   5809                                         Register64 output) {
   5810  WasmAtomicFetchOp64(*this, access, op, value, mem, temp, output);
   5811 }
   5812 
   5813 void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op,
   5814                                     Register64 value, const Address& mem,
   5815                                     Register64 temp, Register64 output) {
   5816  AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output);
   5817 }
   5818 
   5819 void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op,
   5820                                     Register64 value, const BaseIndex& mem,
   5821                                     Register64 temp, Register64 output) {
   5822  AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output);
   5823 }
   5824 
   5825 void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op,
   5826                                      Register64 value, const Address& mem,
   5827                                      Register64 temp) {
   5828  AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp);
   5829 }
   5830 
   5831 void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op,
   5832                                      Register64 value, const BaseIndex& mem,
   5833                                      Register64 temp) {
   5834  AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp);
   5835 }
   5836 
   5837 // ========================================================================
   5838 // JS atomic operations.
   5839 
   5840 template <typename T>
   5841 static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType,
   5842                              Synchronization sync, const T& mem,
   5843                              Register oldval, Register newval, Register temp,
   5844                              AnyRegister output) {
   5845  if (arrayType == Scalar::Uint32) {
   5846    masm.compareExchange(arrayType, sync, mem, oldval, newval, temp);
   5847    masm.convertUInt32ToDouble(temp, output.fpu());
   5848  } else {
   5849    masm.compareExchange(arrayType, sync, mem, oldval, newval, output.gpr());
   5850  }
   5851 }
   5852 
   5853 void MacroAssembler::compareExchangeJS(Scalar::Type arrayType,
   5854                                       Synchronization sync, const Address& mem,
   5855                                       Register oldval, Register newval,
   5856                                       Register temp, AnyRegister output) {
   5857  CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, temp, output);
   5858 }
   5859 
   5860 void MacroAssembler::compareExchangeJS(Scalar::Type arrayType,
   5861                                       Synchronization sync,
   5862                                       const BaseIndex& mem, Register oldval,
   5863                                       Register newval, Register temp,
   5864                                       AnyRegister output) {
   5865  CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, temp, output);
   5866 }
   5867 
   5868 template <typename T>
   5869 static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType,
   5870                             Synchronization sync, const T& mem, Register value,
   5871                             Register temp, AnyRegister output) {
   5872  if (arrayType == Scalar::Uint32) {
   5873    masm.atomicExchange(arrayType, sync, mem, value, temp);
   5874    masm.convertUInt32ToDouble(temp, output.fpu());
   5875  } else {
   5876    masm.atomicExchange(arrayType, sync, mem, value, output.gpr());
   5877  }
   5878 }
   5879 
   5880 void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType,
   5881                                      Synchronization sync, const Address& mem,
   5882                                      Register value, Register temp,
   5883                                      AnyRegister output) {
   5884  AtomicExchangeJS(*this, arrayType, sync, mem, value, temp, output);
   5885 }
   5886 
   5887 void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType,
   5888                                      Synchronization sync,
   5889                                      const BaseIndex& mem, Register value,
   5890                                      Register temp, AnyRegister output) {
   5891  AtomicExchangeJS(*this, arrayType, sync, mem, value, temp, output);
   5892 }
   5893 
   5894 template <typename T>
   5895 static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType,
   5896                            Synchronization sync, AtomicOp op, Register value,
   5897                            const T& mem, Register temp1, Register temp2,
   5898                            AnyRegister output) {
   5899  if (arrayType == Scalar::Uint32) {
   5900    masm.atomicFetchOp(arrayType, sync, op, value, mem, temp2, temp1);
   5901    masm.convertUInt32ToDouble(temp1, output.fpu());
   5902  } else {
   5903    masm.atomicFetchOp(arrayType, sync, op, value, mem, temp1, output.gpr());
   5904  }
   5905 }
   5906 
   5907 void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType,
   5908                                     Synchronization sync, AtomicOp op,
   5909                                     Register value, const Address& mem,
   5910                                     Register temp1, Register temp2,
   5911                                     AnyRegister output) {
   5912  AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, temp1, temp2, output);
   5913 }
   5914 
   5915 void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType,
   5916                                     Synchronization sync, AtomicOp op,
   5917                                     Register value, const BaseIndex& mem,
   5918                                     Register temp1, Register temp2,
   5919                                     AnyRegister output) {
   5920  AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, temp1, temp2, output);
   5921 }
   5922 
   5923 void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType,
   5924                                      Synchronization sync, AtomicOp op,
   5925                                      Register value, const BaseIndex& mem,
   5926                                      Register temp) {
   5927  AtomicEffectOp(*this, nullptr, arrayType, sync, op, value, mem, temp);
   5928 }
   5929 
   5930 void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType,
   5931                                      Synchronization sync, AtomicOp op,
   5932                                      Register value, const Address& mem,
   5933                                      Register temp) {
   5934  AtomicEffectOp(*this, nullptr, arrayType, sync, op, value, mem, temp);
   5935 }
   5936 
   5937 void MacroAssembler::atomicPause() { as_yield(); }
   5938 
   5939 // ========================================================================
   5940 // Primitive atomic operations.
   5941 
   5942 void MacroAssembler::atomicLoad64(Synchronization sync, const Address& mem,
   5943                                  Register64 output) {
   5944  AtomicLoad64(*this, nullptr, sync, mem, output);
   5945 }
   5946 
   5947 void MacroAssembler::atomicLoad64(Synchronization sync, const BaseIndex& mem,
   5948                                  Register64 output) {
   5949  AtomicLoad64(*this, nullptr, sync, mem, output);
   5950 }
   5951 
   5952 void MacroAssembler::atomicStore64(Synchronization sync, const Address& mem,
   5953                                   Register64 value, Register64 temp) {
   5954  AtomicExchange64(*this, nullptr, sync, mem, value, temp);
   5955 }
   5956 
   5957 void MacroAssembler::atomicStore64(Synchronization sync, const BaseIndex& mem,
   5958                                   Register64 value, Register64 temp) {
   5959  AtomicExchange64(*this, nullptr, sync, mem, value, temp);
   5960 }
   5961 
   5962 // ========================================================================
   5963 // Convert floating point.
   5964 
   5965 bool MacroAssembler::convertUInt64ToDoubleNeedsTemp() { return false; }
   5966 
   5967 void MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest,
   5968                                           Register temp) {
   5969  MOZ_ASSERT(temp == Register::Invalid());
   5970  ScratchDoubleScope scratchDouble(*this);
   5971 
   5972  convertUInt32ToDouble(src.high, dest);
   5973  {
   5974    ScratchRegisterScope scratch(*this);
   5975    movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), scratch);
   5976    ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), scratchDouble);
   5977  }
   5978  mulDouble(scratchDouble, dest);
   5979  convertUInt32ToDouble(src.low, scratchDouble);
   5980  addDouble(scratchDouble, dest);
   5981 }
   5982 
   5983 void MacroAssembler::convertInt64ToDouble(Register64 src, FloatRegister dest) {
   5984  ScratchDoubleScope scratchDouble(*this);
   5985 
   5986  convertInt32ToDouble(src.high, dest);
   5987  {
   5988    ScratchRegisterScope scratch(*this);
   5989    movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), scratch);
   5990    ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), scratchDouble);
   5991  }
   5992  mulDouble(scratchDouble, dest);
   5993  convertUInt32ToDouble(src.low, scratchDouble);
   5994  addDouble(scratchDouble, dest);
   5995 }
   5996 
   5997 void MacroAssembler::convertIntPtrToDouble(Register src, FloatRegister dest) {
   5998  convertInt32ToDouble(src, dest);
   5999 }
   6000 
   6001 extern "C" {
   6002 extern MOZ_EXPORT int64_t __aeabi_idivmod(int, int);
   6003 extern MOZ_EXPORT int64_t __aeabi_uidivmod(int, int);
   6004 }
   6005 
   6006 static void EmitRemainderOrQuotient(bool isRemainder, MacroAssembler& masm,
   6007                                    Register lhs, Register rhs, Register output,
   6008                                    bool isUnsigned,
   6009                                    const LiveRegisterSet& volatileLiveRegs) {
   6010  if (ARMFlags::HasIDIV()) {
   6011    if (isRemainder) {
   6012      masm.remainder32(lhs, rhs, output, isUnsigned);
   6013    } else {
   6014      masm.quotient32(lhs, rhs, output, isUnsigned);
   6015    }
   6016  } else {
   6017    // Ensure that the output registers are saved and restored properly.
   6018    LiveRegisterSet liveRegs = volatileLiveRegs;
   6019    liveRegs.addUnchecked(ReturnRegVal0);
   6020    liveRegs.addUnchecked(ReturnRegVal1);
   6021 
   6022    masm.PushRegsInMask(liveRegs);
   6023 
   6024    using Fn = int64_t (*)(int, int);
   6025    {
   6026      ScratchRegisterScope scratch(masm);
   6027      masm.setupUnalignedABICall(scratch);
   6028    }
   6029    masm.passABIArg(lhs);
   6030    masm.passABIArg(rhs);
   6031    if (isUnsigned) {
   6032      masm.callWithABI<Fn, __aeabi_uidivmod>(
   6033          ABIType::Int64, CheckUnsafeCallWithABI::DontCheckOther);
   6034    } else {
   6035      masm.callWithABI<Fn, __aeabi_idivmod>(
   6036          ABIType::Int64, CheckUnsafeCallWithABI::DontCheckOther);
   6037    }
   6038    if (isRemainder) {
   6039      masm.mov(ReturnRegVal1, output);
   6040    } else {
   6041      masm.mov(ReturnRegVal0, output);
   6042    }
   6043 
   6044    LiveRegisterSet ignore;
   6045    ignore.add(output);
   6046    masm.PopRegsInMaskIgnore(liveRegs, ignore);
   6047  }
   6048 }
   6049 
   6050 void MacroAssembler::flexibleQuotient32(
   6051    Register lhs, Register rhs, Register dest, bool isUnsigned,
   6052    const LiveRegisterSet& volatileLiveRegs) {
   6053  EmitRemainderOrQuotient(false, *this, lhs, rhs, dest, isUnsigned,
   6054                          volatileLiveRegs);
   6055 }
   6056 
   6057 void MacroAssembler::flexibleQuotientPtr(
   6058    Register lhs, Register rhs, Register dest, bool isUnsigned,
   6059    const LiveRegisterSet& volatileLiveRegs) {
   6060  flexibleQuotient32(lhs, rhs, dest, isUnsigned, volatileLiveRegs);
   6061 }
   6062 
   6063 void MacroAssembler::flexibleRemainder32(
   6064    Register lhs, Register rhs, Register dest, bool isUnsigned,
   6065    const LiveRegisterSet& volatileLiveRegs) {
   6066  EmitRemainderOrQuotient(true, *this, lhs, rhs, dest, isUnsigned,
   6067                          volatileLiveRegs);
   6068 }
   6069 
   6070 void MacroAssembler::flexibleRemainderPtr(
   6071    Register lhs, Register rhs, Register dest, bool isUnsigned,
   6072    const LiveRegisterSet& volatileLiveRegs) {
   6073  flexibleRemainder32(lhs, rhs, dest, isUnsigned, volatileLiveRegs);
   6074 }
   6075 
   6076 void MacroAssembler::flexibleDivMod32(Register lhs, Register rhs,
   6077                                      Register divOutput, Register remOutput,
   6078                                      bool isUnsigned,
   6079                                      const LiveRegisterSet& volatileLiveRegs) {
   6080  MOZ_ASSERT(lhs != divOutput && lhs != remOutput, "lhs is preserved");
   6081  MOZ_ASSERT(rhs != divOutput && rhs != remOutput, "rhs is preserved");
   6082 
   6083  if (ARMFlags::HasIDIV()) {
   6084    if (isUnsigned) {
   6085      as_udiv(divOutput, lhs, rhs);
   6086    } else {
   6087      as_sdiv(divOutput, lhs, rhs);
   6088    }
   6089    as_mls(remOutput, lhs, divOutput, rhs);
   6090  } else {
   6091    // Ensure that the output registers are saved and restored properly.
   6092    LiveRegisterSet liveRegs = volatileLiveRegs;
   6093    liveRegs.addUnchecked(ReturnRegVal0);
   6094    liveRegs.addUnchecked(ReturnRegVal1);
   6095 
   6096    PushRegsInMask(liveRegs);
   6097 
   6098    using Fn = int64_t (*)(int, int);
   6099    {
   6100      ScratchRegisterScope scratch(*this);
   6101      setupUnalignedABICall(scratch);
   6102    }
   6103    passABIArg(lhs);
   6104    passABIArg(rhs);
   6105    if (isUnsigned) {
   6106      callWithABI<Fn, __aeabi_uidivmod>(ABIType::Int64,
   6107                                        CheckUnsafeCallWithABI::DontCheckOther);
   6108    } else {
   6109      callWithABI<Fn, __aeabi_idivmod>(ABIType::Int64,
   6110                                       CheckUnsafeCallWithABI::DontCheckOther);
   6111    }
   6112    moveRegPair(ReturnRegVal0, ReturnRegVal1, divOutput, remOutput);
   6113 
   6114    LiveRegisterSet ignore;
   6115    ignore.add(remOutput);
   6116    ignore.add(divOutput);
   6117    PopRegsInMaskIgnore(liveRegs, ignore);
   6118  }
   6119 }
   6120 
   6121 CodeOffset MacroAssembler::moveNearAddressWithPatch(Register dest) {
   6122  return movWithPatch(ImmPtr(nullptr), dest);
   6123 }
   6124 
   6125 void MacroAssembler::patchNearAddressMove(CodeLocationLabel loc,
   6126                                          CodeLocationLabel target) {
   6127  PatchDataWithValueCheck(loc, ImmPtr(target.raw()), ImmPtr(nullptr));
   6128 }
   6129 
   6130 // ========================================================================
   6131 // Spectre Mitigations.
   6132 
   6133 void MacroAssembler::speculationBarrier() {
   6134  // Spectre mitigation recommended by ARM for cases where csel/cmov cannot be
   6135  // used.
   6136  as_csdb();
   6137 }
   6138 
   6139 void MacroAssembler::floorFloat32ToInt32(FloatRegister src, Register dest,
   6140                                         Label* fail) {
   6141  floorf(src, dest, fail);
   6142 }
   6143 
   6144 void MacroAssembler::floorDoubleToInt32(FloatRegister src, Register dest,
   6145                                        Label* fail) {
   6146  floor(src, dest, fail);
   6147 }
   6148 
   6149 void MacroAssembler::ceilFloat32ToInt32(FloatRegister src, Register dest,
   6150                                        Label* fail) {
   6151  ceilf(src, dest, fail);
   6152 }
   6153 
   6154 void MacroAssembler::ceilDoubleToInt32(FloatRegister src, Register dest,
   6155                                       Label* fail) {
   6156  ceil(src, dest, fail);
   6157 }
   6158 
   6159 void MacroAssembler::roundFloat32ToInt32(FloatRegister src, Register dest,
   6160                                         FloatRegister temp, Label* fail) {
   6161  roundf(src, dest, fail, temp);
   6162 }
   6163 
   6164 void MacroAssembler::roundDoubleToInt32(FloatRegister src, Register dest,
   6165                                        FloatRegister temp, Label* fail) {
   6166  round(src, dest, fail, temp);
   6167 }
   6168 
   6169 void MacroAssembler::truncFloat32ToInt32(FloatRegister src, Register dest,
   6170                                         Label* fail) {
   6171  truncf(src, dest, fail);
   6172 }
   6173 
   6174 void MacroAssembler::truncDoubleToInt32(FloatRegister src, Register dest,
   6175                                        Label* fail) {
   6176  trunc(src, dest, fail);
   6177 }
   6178 
   6179 void MacroAssembler::nearbyIntDouble(RoundingMode mode, FloatRegister src,
   6180                                     FloatRegister dest) {
   6181  MOZ_CRASH("not supported on this platform");
   6182 }
   6183 
   6184 void MacroAssembler::nearbyIntFloat32(RoundingMode mode, FloatRegister src,
   6185                                      FloatRegister dest) {
   6186  MOZ_CRASH("not supported on this platform");
   6187 }
   6188 
   6189 void MacroAssembler::copySignDouble(FloatRegister lhs, FloatRegister rhs,
   6190                                    FloatRegister output) {
   6191  ScratchRegisterScope scratch(*this);
   6192 
   6193  // Transfer top word of |rhs| into GPR.
   6194  as_vxfer(scratch, InvalidReg, rhs, Assembler::FloatToCore, Assembler::Always,
   6195           1);
   6196 
   6197  // Move |lhs| with sign-bit cleared into |output|.
   6198  ma_vabs(lhs, output);
   6199 
   6200  // Negate |output| if sign bit of |rhs| is set.
   6201  as_cmp(scratch, Imm8(0));
   6202  ma_vneg(output, output, Assembler::LessThan);
   6203 }
   6204 
   6205 void MacroAssembler::copySignFloat32(FloatRegister lhs, FloatRegister rhs,
   6206                                     FloatRegister output) {
   6207  ScratchRegisterScope scratch(*this);
   6208 
   6209  // Transfer |rhs| into GPR.
   6210  ma_vxfer(rhs, scratch);
   6211 
   6212  // Move |lhs| with sign-bit cleared into |output|.
   6213  ma_vabs_f32(lhs, output);
   6214 
   6215  // Negate |output| if sign bit of |rhs| is set.
   6216  as_cmp(scratch, Imm8(0));
   6217  ma_vneg_f32(output, output, Assembler::LessThan);
   6218 }
   6219 
   6220 void MacroAssembler::shiftIndex32AndAdd(Register indexTemp32, int shift,
   6221                                        Register pointer) {
   6222  if (IsShiftInScaleRange(shift)) {
   6223    computeEffectiveAddress(
   6224        BaseIndex(pointer, indexTemp32, ShiftToScale(shift)), pointer);
   6225    return;
   6226  }
   6227  lshift32(Imm32(shift), indexTemp32);
   6228  addPtr(indexTemp32, pointer);
   6229 }
   6230 
   6231 void MacroAssembler::wasmMarkCallAsSlow() { ma_and(lr, lr, lr); }
   6232 
   6233 const int32_t SlowCallMarker = 0xe00ee00e;
   6234 
   6235 void MacroAssembler::wasmCheckSlowCallsite(Register ra, Label* notSlow,
   6236                                           Register temp1, Register temp2) {
   6237  MOZ_ASSERT(temp1 != temp2);
   6238  // Check if RA has slow marker.
   6239  load32(Address(ra, 0), temp2);
   6240  ma_mov(Imm32(SlowCallMarker), temp1, Always);
   6241  ma_cmp(temp2, temp1);
   6242  j(Assembler::NotEqual, notSlow);
   6243 }
   6244 
   6245 CodeOffset MacroAssembler::wasmMarkedSlowCall(const wasm::CallSiteDesc& desc,
   6246                                              const Register reg) {
   6247  AutoForbidPoolsAndNops afp(this, 2);
   6248  CodeOffset offset = call(desc, reg);
   6249  wasmMarkCallAsSlow();
   6250  return offset;
   6251 }
   6252 
   6253 //}}} check_macroassembler_style
   6254 
   6255 void MacroAssemblerARM::wasmTruncateToInt32(FloatRegister input,
   6256                                            Register output, MIRType fromType,
   6257                                            bool isUnsigned, bool isSaturating,
   6258                                            Label* oolEntry) {
   6259  ScratchDoubleScope scratchScope(asMasm());
   6260  ScratchRegisterScope scratchReg(asMasm());
   6261  FloatRegister scratch = scratchScope.uintOverlay();
   6262 
   6263  // ARM conversion instructions clamp the value to ensure it fits within the
   6264  // target's type bounds, so every time we see those, we need to check the
   6265  // input. A NaN check is not necessary because NaN is converted to zero and
   6266  // on a zero result we branch out of line to do further processing anyway.
   6267  if (isUnsigned) {
   6268    if (fromType == MIRType::Double) {
   6269      ma_vcvt_F64_U32(input, scratch);
   6270    } else if (fromType == MIRType::Float32) {
   6271      ma_vcvt_F32_U32(input, scratch);
   6272    } else {
   6273      MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
   6274    }
   6275 
   6276    ma_vxfer(scratch, output);
   6277 
   6278    if (!isSaturating) {
   6279      // int32_t(UINT32_MAX) == -1.
   6280      ma_cmp(output, Imm32(-1), scratchReg);
   6281      as_cmp(output, Imm8(0), Assembler::NotEqual);
   6282      ma_b(oolEntry, Assembler::Equal);
   6283    }
   6284 
   6285    return;
   6286  }
   6287 
   6288  // vcvt* converts NaN into 0, so check for NaNs here.
   6289  if (!isSaturating) {
   6290    if (fromType == MIRType::Double) {
   6291      asMasm().compareDouble(input, input);
   6292    } else if (fromType == MIRType::Float32) {
   6293      asMasm().compareFloat(input, input);
   6294    } else {
   6295      MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
   6296    }
   6297 
   6298    ma_b(oolEntry, Assembler::VFP_Unordered);
   6299  }
   6300 
   6301  scratch = scratchScope.sintOverlay();
   6302 
   6303  if (fromType == MIRType::Double) {
   6304    ma_vcvt_F64_I32(input, scratch);
   6305  } else if (fromType == MIRType::Float32) {
   6306    ma_vcvt_F32_I32(input, scratch);
   6307  } else {
   6308    MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
   6309  }
   6310 
   6311  ma_vxfer(scratch, output);
   6312 
   6313  if (!isSaturating) {
   6314    ma_cmp(output, Imm32(INT32_MAX), scratchReg);
   6315    ma_cmp(output, Imm32(INT32_MIN), scratchReg, Assembler::NotEqual);
   6316    ma_b(oolEntry, Assembler::Equal);
   6317  }
   6318 }
   6319 
   6320 void MacroAssemblerARM::outOfLineWasmTruncateToIntCheck(
   6321    FloatRegister input, MIRType fromType, MIRType toType, TruncFlags flags,
   6322    Label* rejoin, const wasm::TrapSiteDesc& trapSiteDesc) {
   6323  // On ARM, saturating truncation codegen handles saturating itself rather
   6324  // than relying on out-of-line fixup code.
   6325  if (flags & TRUNC_SATURATING) {
   6326    return;
   6327  }
   6328 
   6329  bool isUnsigned = flags & TRUNC_UNSIGNED;
   6330  ScratchDoubleScope scratchScope(asMasm());
   6331  FloatRegister scratch;
   6332 
   6333  // Eagerly take care of NaNs.
   6334  Label inputIsNaN;
   6335  if (fromType == MIRType::Double) {
   6336    asMasm().branchDouble(Assembler::DoubleUnordered, input, input,
   6337                          &inputIsNaN);
   6338  } else if (fromType == MIRType::Float32) {
   6339    asMasm().branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN);
   6340  } else {
   6341    MOZ_CRASH("unexpected type in visitOutOfLineWasmTruncateCheck");
   6342  }
   6343 
   6344  // Handle special values.
   6345  Label fail;
   6346 
   6347  // By default test for the following inputs and bail:
   6348  // signed:   ] -Inf, INTXX_MIN - 1.0 ] and [ INTXX_MAX + 1.0 : +Inf [
   6349  // unsigned: ] -Inf, -1.0 ] and [ UINTXX_MAX + 1.0 : +Inf [
   6350  // Note: we cannot always represent those exact values. As a result
   6351  // this changes the actual comparison a bit.
   6352  double minValue, maxValue;
   6353  Assembler::DoubleCondition minCond = Assembler::DoubleLessThanOrEqual;
   6354  Assembler::DoubleCondition maxCond = Assembler::DoubleGreaterThanOrEqual;
   6355  if (toType == MIRType::Int64) {
   6356    if (isUnsigned) {
   6357      minValue = -1;
   6358      maxValue = double(UINT64_MAX) + 1.0;
   6359    } else {
   6360      // In the float32/double range there exists no value between
   6361      // INT64_MIN and INT64_MIN - 1.0. Making INT64_MIN the lower-bound.
   6362      minValue = double(INT64_MIN);
   6363      minCond = Assembler::DoubleLessThan;
   6364      maxValue = double(INT64_MAX) + 1.0;
   6365    }
   6366  } else {
   6367    if (isUnsigned) {
   6368      minValue = -1;
   6369      maxValue = double(UINT32_MAX) + 1.0;
   6370    } else {
   6371      if (fromType == MIRType::Float32) {
   6372        // In the float32 range there exists no value between
   6373        // INT32_MIN and INT32_MIN - 1.0. Making INT32_MIN the lower-bound.
   6374        minValue = double(INT32_MIN);
   6375        minCond = Assembler::DoubleLessThan;
   6376      } else {
   6377        minValue = double(INT32_MIN) - 1.0;
   6378      }
   6379      maxValue = double(INT32_MAX) + 1.0;
   6380    }
   6381  }
   6382 
   6383  if (fromType == MIRType::Double) {
   6384    scratch = scratchScope.doubleOverlay();
   6385    asMasm().loadConstantDouble(minValue, scratch);
   6386    asMasm().branchDouble(minCond, input, scratch, &fail);
   6387 
   6388    asMasm().loadConstantDouble(maxValue, scratch);
   6389    asMasm().branchDouble(maxCond, input, scratch, &fail);
   6390  } else {
   6391    MOZ_ASSERT(fromType == MIRType::Float32);
   6392    scratch = scratchScope.singleOverlay();
   6393    asMasm().loadConstantFloat32(float(minValue), scratch);
   6394    asMasm().branchFloat(minCond, input, scratch, &fail);
   6395 
   6396    asMasm().loadConstantFloat32(float(maxValue), scratch);
   6397    asMasm().branchFloat(maxCond, input, scratch, &fail);
   6398  }
   6399 
   6400  // We had an actual correct value, get back to where we were.
   6401  ma_b(rejoin);
   6402 
   6403  // Handle errors.
   6404  bind(&fail);
   6405  asMasm().wasmTrap(wasm::Trap::IntegerOverflow, trapSiteDesc);
   6406 
   6407  bind(&inputIsNaN);
   6408  asMasm().wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc);
   6409 }
   6410 
   6411 void MacroAssemblerARM::wasmLoadImpl(const wasm::MemoryAccessDesc& access,
   6412                                     Register memoryBase, Register ptr,
   6413                                     Register ptrScratch, AnyRegister output,
   6414                                     Register64 out64) {
   6415  MOZ_ASSERT(memoryBase != ptr);
   6416  MOZ_ASSERT_IF(out64 != Register64::Invalid(), memoryBase != out64.high);
   6417  MOZ_ASSERT_IF(out64 != Register64::Invalid(), memoryBase != out64.low);
   6418  MOZ_ASSERT(ptr == ptrScratch);
   6419  MOZ_ASSERT(!access.isZeroExtendSimd128Load());
   6420  MOZ_ASSERT(!access.isSplatSimd128Load());
   6421  MOZ_ASSERT(!access.isWidenSimd128Load());
   6422 
   6423  access.assertOffsetInGuardPages();
   6424  uint32_t offset = access.offset32();
   6425 
   6426  Scalar::Type type = access.type();
   6427 
   6428  // Maybe add the offset.
   6429  if (offset || type == Scalar::Int64) {
   6430    ScratchRegisterScope scratch(asMasm());
   6431    if (offset) {
   6432      ma_add(Imm32(offset), ptr, scratch);
   6433    }
   6434  }
   6435 
   6436  bool isSigned = Scalar::isSignedIntType(type);
   6437  unsigned byteSize = access.byteSize();
   6438 
   6439  // NOTE: the generated code must match the assembly code in gen_load in
   6440  // GenerateAtomicOperations.py
   6441  asMasm().memoryBarrierBefore(access.sync());
   6442 
   6443  BufferOffset load;
   6444  if (out64 != Register64::Invalid()) {
   6445    if (type == Scalar::Int64) {
   6446      static_assert(INT64LOW_OFFSET == 0);
   6447 
   6448      load = ma_dataTransferN(IsLoad, 32, /* signed = */ false, memoryBase, ptr,
   6449                              out64.low);
   6450      append(access, js::wasm::TrapMachineInsn::Load32,
   6451             FaultingCodeOffset(load.getOffset()));
   6452 
   6453      as_add(ptr, ptr, Imm8(INT64HIGH_OFFSET));
   6454 
   6455      load =
   6456          ma_dataTransferN(IsLoad, 32, isSigned, memoryBase, ptr, out64.high);
   6457      append(access, js::wasm::TrapMachineInsn::Load32,
   6458             FaultingCodeOffset(load.getOffset()));
   6459    } else {
   6460      load = ma_dataTransferN(IsLoad, byteSize * 8, isSigned, memoryBase, ptr,
   6461                              out64.low);
   6462      append(access, js::wasm::TrapMachineInsnForLoad(byteSize),
   6463             FaultingCodeOffset(load.getOffset()));
   6464 
   6465      if (isSigned) {
   6466        ma_asr(Imm32(31), out64.low, out64.high);
   6467      } else {
   6468        ma_mov(Imm32(0), out64.high);
   6469      }
   6470    }
   6471  } else {
   6472    bool isFloat = output.isFloat();
   6473    if (isFloat) {
   6474      MOZ_ASSERT((byteSize == 4) == output.fpu().isSingle());
   6475      ScratchRegisterScope scratch(asMasm());
   6476      FloatRegister dest = output.fpu();
   6477      ma_add(memoryBase, ptr, scratch);
   6478 
   6479      // FP loads can't use VLDR as that has stringent alignment checks and will
   6480      // SIGBUS on unaligned accesses.  Choose a different strategy depending on
   6481      // the available hardware. We don't gate Wasm on the presence of NEON.
   6482      if (ARMFlags::HasNEON()) {
   6483        // NEON available: The VLD1 multiple-single-elements variant will only
   6484        // trap if SCTRL.A==1, but we already assume (for integer accesses) that
   6485        // the hardware/OS handles that transparently.
   6486        //
   6487        // An additional complication is that if we're targeting the high single
   6488        // then an unaligned load is not possible, and we may need to go via the
   6489        // FPR scratch.
   6490        if (byteSize == 4 && dest.code() & 1) {
   6491          ScratchFloat32Scope fscratch(asMasm());
   6492          load = as_vldr_unaligned(fscratch, scratch);
   6493          as_vmov(dest, fscratch);
   6494        } else {
   6495          load = as_vldr_unaligned(dest, scratch);
   6496        }
   6497        append(access, js::wasm::TrapMachineInsnForLoad(byteSize),
   6498               FaultingCodeOffset(load.getOffset()));
   6499      } else {
   6500        // NEON not available: Load to GPR scratch, move to FPR destination.  We
   6501        // don't have adjacent scratches for the f64, so use individual LDRs,
   6502        // not LDRD.
   6503        SecondScratchRegisterScope scratch2(asMasm());
   6504        if (byteSize == 4) {
   6505          load = as_dtr(IsLoad, 32, Offset, scratch2,
   6506                        DTRAddr(scratch, DtrOffImm(0)), Always);
   6507          as_vxfer(scratch2, InvalidReg, VFPRegister(dest), CoreToFloat,
   6508                   Always);
   6509          append(access, js::wasm::TrapMachineInsn::Load32,
   6510                 FaultingCodeOffset(load.getOffset()));
   6511        } else {
   6512          // The trap information is associated with the load of the high word,
   6513          // which must be done first.  FIXME sewardj 20230825: is it still
   6514          // safe to skip the low word, now that we support wasm-gc?
   6515          load = as_dtr(IsLoad, 32, Offset, scratch2,
   6516                        DTRAddr(scratch, DtrOffImm(4)), Always);
   6517          append(access, js::wasm::TrapMachineInsn::Load32,
   6518                 FaultingCodeOffset(load.getOffset()));
   6519          as_dtr(IsLoad, 32, Offset, scratch, DTRAddr(scratch, DtrOffImm(0)),
   6520                 Always);
   6521          as_vxfer(scratch, scratch2, VFPRegister(dest), CoreToFloat, Always);
   6522        }
   6523      }
   6524    } else {
   6525      load = ma_dataTransferN(IsLoad, byteSize * 8, isSigned, memoryBase, ptr,
   6526                              output.gpr());
   6527      append(access, js::wasm::TrapMachineInsnForLoad(byteSize),
   6528             FaultingCodeOffset(load.getOffset()));
   6529    }
   6530  }
   6531 
   6532  asMasm().memoryBarrierAfter(access.sync());
   6533 }
   6534 
   6535 void MacroAssemblerARM::wasmStoreImpl(const wasm::MemoryAccessDesc& access,
   6536                                      AnyRegister value, Register64 val64,
   6537                                      Register memoryBase, Register ptr,
   6538                                      Register ptrScratch) {
   6539  static_assert(INT64LOW_OFFSET == 0);
   6540  static_assert(INT64HIGH_OFFSET == 4);
   6541 
   6542  MOZ_ASSERT(ptr == ptrScratch);
   6543 
   6544  access.assertOffsetInGuardPages();
   6545  uint32_t offset = access.offset32();
   6546  unsigned byteSize = access.byteSize();
   6547  Scalar::Type type = access.type();
   6548 
   6549  // Maybe add the offset.
   6550  if (offset || type == Scalar::Int64) {
   6551    ScratchRegisterScope scratch(asMasm());
   6552    // We need to store the high word of an Int64 first, so always adjust the
   6553    // pointer to point to the high word in this case.  The adjustment is always
   6554    // OK because wasmMaxOffsetGuardLimit is computed so that we can add up to
   6555    // sizeof(LargestValue)-1 without skipping past the guard page, and we
   6556    // assert above that offset < wasmMaxOffsetGuardLimit.
   6557    if (type == Scalar::Int64) {
   6558      offset += INT64HIGH_OFFSET;
   6559    }
   6560    if (offset) {
   6561      ma_add(Imm32(offset), ptr, scratch);
   6562    }
   6563  }
   6564 
   6565  // NOTE: the generated code must match the assembly code in gen_store in
   6566  // GenerateAtomicOperations.py
   6567  asMasm().memoryBarrierBefore(access.sync());
   6568 
   6569  BufferOffset store;
   6570  if (type == Scalar::Int64) {
   6571    store = ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ false,
   6572                             memoryBase, ptr, val64.high);
   6573    append(access, js::wasm::TrapMachineInsn::Store32,
   6574           FaultingCodeOffset(store.getOffset()));
   6575 
   6576    as_sub(ptr, ptr, Imm8(INT64HIGH_OFFSET));
   6577 
   6578    store = ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ true,
   6579                             memoryBase, ptr, val64.low);
   6580    append(access, js::wasm::TrapMachineInsn::Store32,
   6581           FaultingCodeOffset(store.getOffset()));
   6582  } else {
   6583    if (value.isFloat()) {
   6584      ScratchRegisterScope scratch(asMasm());
   6585      FloatRegister val = value.fpu();
   6586      MOZ_ASSERT((byteSize == 4) == val.isSingle());
   6587      ma_add(memoryBase, ptr, scratch);
   6588 
   6589      // See comments above at wasmLoadImpl for more about this logic.
   6590      if (ARMFlags::HasNEON()) {
   6591        if (byteSize == 4 && (val.code() & 1)) {
   6592          ScratchFloat32Scope fscratch(asMasm());
   6593          as_vmov(fscratch, val);
   6594          store = as_vstr_unaligned(fscratch, scratch);
   6595        } else {
   6596          store = as_vstr_unaligned(val, scratch);
   6597        }
   6598        append(access, js::wasm::TrapMachineInsnForStore(byteSize),
   6599               FaultingCodeOffset(store.getOffset()));
   6600      } else {
   6601        // NEON not available: Move FPR to GPR scratch, store GPR.  We have only
   6602        // one scratch to hold the value, so for f64 we must do two separate
   6603        // moves.  That's OK - this is really a corner case.  If we really cared
   6604        // we would pass in a temp to avoid the second move.
   6605        SecondScratchRegisterScope scratch2(asMasm());
   6606        if (byteSize == 4) {
   6607          as_vxfer(scratch2, InvalidReg, VFPRegister(val), FloatToCore, Always);
   6608          store = as_dtr(IsStore, 32, Offset, scratch2,
   6609                         DTRAddr(scratch, DtrOffImm(0)), Always);
   6610          append(access, js::wasm::TrapMachineInsn::Store32,
   6611                 FaultingCodeOffset(store.getOffset()));
   6612        } else {
   6613          // The trap information is associated with the store of the high word,
   6614          // which must be done first.  FIXME sewardj 20230825: is it still
   6615          // safe to skip the low word, now that we support wasm-gc?
   6616          as_vxfer(scratch2, InvalidReg, VFPRegister(val).singleOverlay(1),
   6617                   FloatToCore, Always);
   6618          store = as_dtr(IsStore, 32, Offset, scratch2,
   6619                         DTRAddr(scratch, DtrOffImm(4)), Always);
   6620          append(access, js::wasm::TrapMachineInsn::Store32,
   6621                 FaultingCodeOffset(store.getOffset()));
   6622          as_vxfer(scratch2, InvalidReg, VFPRegister(val).singleOverlay(0),
   6623                   FloatToCore, Always);
   6624          as_dtr(IsStore, 32, Offset, scratch2, DTRAddr(scratch, DtrOffImm(0)),
   6625                 Always);
   6626        }
   6627      }
   6628    } else {
   6629      bool isSigned = type == Scalar::Uint32 ||
   6630                      type == Scalar::Int32;  // see AsmJSStoreHeap;
   6631      Register val = value.gpr();
   6632 
   6633      store = ma_dataTransferN(IsStore, 8 * byteSize /* bits */, isSigned,
   6634                               memoryBase, ptr, val);
   6635      append(access, js::wasm::TrapMachineInsnForStore(byteSize),
   6636             FaultingCodeOffset(store.getOffset()));
   6637    }
   6638  }
   6639 
   6640  asMasm().memoryBarrierAfter(access.sync());
   6641 }