Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/js/src/jit/arm64/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 77 kB image not shown  

Quelle  MacroAssembler-arm64.h   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#ifndef jit_arm64_MacroAssembler_arm64_h
#define jit_arm64_MacroAssembler_arm64_h

#include "jit/arm64/Assembler-arm64.h"
#include "jit/arm64/vixl/Debugger-vixl.h"
#include "jit/arm64/vixl/MacroAssembler-vixl.h"
#include "jit/AtomicOp.h"
#include "jit/MoveResolver.h"
#include "vm/BigIntType.h"  // JS::BigInt
#include "wasm/WasmBuiltins.h"

#ifdef _M_ARM64
#  ifdef move32
#    undef move32
#  endif
#  ifdef move64
#    undef move64
#  endif
#endif

namespace js {
namespace jit {

// Import VIXL operands directly into the jit namespace for shared code.
using vixl::MemOperand;
using vixl::Operand;

struct ImmShiftedTag : public ImmWord {
  explicit ImmShiftedTag(JSValueType type)
      : ImmWord(uintptr_t(JSValueShiftedTag(JSVAL_TYPE_TO_SHIFTED_TAG(type)))) {
  }
};

struct ImmTag : public Imm32 {
  explicit ImmTag(JSValueTag tag) : Imm32(tag) {}
};

class ScratchTagScope;

class MacroAssemblerCompat : public vixl::MacroAssembler {
 public:
  using Condition = vixl::Condition;

 private:
  // Perform a downcast. Should be removed by Bug 996602.
  js::jit::MacroAssembler& asMasm();
  const js::jit::MacroAssembler& asMasm() const;

 public:
  // Restrict to only VIXL-internal functions.
  vixl::MacroAssembler& asVIXL();
  const MacroAssembler& asVIXL() const;

 protected:
  bool enoughMemory_;

  MacroAssemblerCompat() : vixl::MacroAssembler(), enoughMemory_(true) {}

 protected:
  MoveResolver moveResolver_;

 public:
  bool oom() const { return Assembler::oom() || !enoughMemory_; }
  static ARMRegister toARMRegister(RegisterOrSP r, size_t size) {
    if (IsHiddenSP(r)) {
      MOZ_ASSERT(size == 64);
      return sp;
    }
    return ARMRegister(AsRegister(r), size);
  }
  static MemOperand toMemOperand(const Address& a) {
    return MemOperand(toARMRegister(a.base, 64), a.offset);
  }
  FaultingCodeOffset doBaseIndex(const vixl::CPURegister& rt,
                                 const BaseIndex& addr, vixl::LoadStoreOp op) {
    const ARMRegister base = toARMRegister(addr.base, 64);
    const ARMRegister index = ARMRegister(addr.index, 64);
    const unsigned scale = addr.scale;

    if (!addr.offset &&
        (!scale || scale == static_cast<unsigned>(CalcLSDataSize(op)))) {
      return LoadStoreMacro(rt, MemOperand(base, index, vixl::LSL, scale), op);
    }

    vixl::UseScratchRegisterScope temps(this);
    ARMRegister scratch64 = temps.AcquireX();
    MOZ_ASSERT(!scratch64.Is(rt));
    MOZ_ASSERT(!scratch64.Is(base));
    MOZ_ASSERT(!scratch64.Is(index));

    Add(scratch64, base, Operand(index, vixl::LSL, scale));
    return LoadStoreMacro(rt, MemOperand(scratch64, addr.offset), op);
  }
  void push(FloatRegister f) {
    vixl::MacroAssembler::Push(ARMFPRegister(f, 64));
  }
  void push(ARMFPRegister f) { vixl::MacroAssembler::Push(f); }
  void push(Imm32 imm) {
    if (imm.value == 0) {
      vixl::MacroAssembler::Push(vixl::xzr);
    } else {
      vixl::UseScratchRegisterScope temps(this);
      const ARMRegister scratch64 = temps.AcquireX();
      move32(imm, scratch64.asUnsized());
      vixl::MacroAssembler::Push(scratch64);
    }
  }
  void push(ImmWord imm) {
    if (imm.value == 0) {
      vixl::MacroAssembler::Push(vixl::xzr);
    } else {
      vixl::UseScratchRegisterScope temps(this);
      const ARMRegister scratch64 = temps.AcquireX();
      Mov(scratch64, imm.value);
      vixl::MacroAssembler::Push(scratch64);
    }
  }
  void push(ImmPtr imm) {
    if (imm.value == nullptr) {
      vixl::MacroAssembler::Push(vixl::xzr);
    } else {
      vixl::UseScratchRegisterScope temps(this);
      const ARMRegister scratch64 = temps.AcquireX();
      movePtr(imm, scratch64.asUnsized());
      vixl::MacroAssembler::Push(scratch64);
    }
  }
  void push(ImmGCPtr imm) {
    if (imm.value == nullptr) {
      vixl::MacroAssembler::Push(vixl::xzr);
    } else {
      vixl::UseScratchRegisterScope temps(this);
      const ARMRegister scratch64 = temps.AcquireX();
      movePtr(imm, scratch64.asUnsized());
      vixl::MacroAssembler::Push(scratch64);
    }
  }
  void push(ARMRegister reg) { vixl::MacroAssembler::Push(reg); }
  void push(Address a) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch64 = temps.AcquireX();
    MOZ_ASSERT(a.base != scratch64.asUnsized());
    loadPtr(a, scratch64.asUnsized());
    vixl::MacroAssembler::Push(scratch64);
  }

  // Push registers.
  void push(Register reg) { vixl::MacroAssembler::Push(ARMRegister(reg, 64)); }
  void push(RegisterOrSP reg) {
    if (IsHiddenSP(reg)) {
      vixl::MacroAssembler::Push(sp);
    }
    vixl::MacroAssembler::Push(toARMRegister(reg, 64));
  }
  void push(Register r0, Register r1) {
    vixl::MacroAssembler::Push(ARMRegister(r0, 64), ARMRegister(r1, 64));
  }
  void push(Register r0, Register r1, Register r2) {
    vixl::MacroAssembler::Push(ARMRegister(r0, 64), ARMRegister(r1, 64),
                               ARMRegister(r2, 64));
  }
  void push(Register r0, Register r1, Register r2, Register r3) {
    vixl::MacroAssembler::Push(ARMRegister(r0, 64), ARMRegister(r1, 64),
                               ARMRegister(r2, 64), ARMRegister(r3, 64));
  }
  void push(Register r0, ARMRegister r1) {
    vixl::MacroAssembler::Push(ARMRegister(r0, 64), r1);
  }
  void push(ARMFPRegister r0, ARMFPRegister r1, ARMFPRegister r2,
            ARMFPRegister r3) {
    vixl::MacroAssembler::Push(r0, r1, r2, r3);
  }

  // Pop registers.
  void pop(Register reg) { vixl::MacroAssembler::Pop(ARMRegister(reg, 64)); }
  void pop(Register r0, Register r1) {
    vixl::MacroAssembler::Pop(ARMRegister(r0, 64), ARMRegister(r1, 64));
  }
  void pop(Register r0, Register r1, Register r2) {
    vixl::MacroAssembler::Pop(ARMRegister(r0, 64), ARMRegister(r1, 64),
                              ARMRegister(r2, 64));
  }
  void pop(Register r0, Register r1, Register r2, Register r3) {
    vixl::MacroAssembler::Pop(ARMRegister(r0, 64), ARMRegister(r1, 64),
                              ARMRegister(r2, 64), ARMRegister(r3, 64));
  }
  void pop(ARMFPRegister r0, ARMFPRegister r1, ARMFPRegister r2,
           ARMFPRegister r3) {
    vixl::MacroAssembler::Pop(r0, r1, r2, r3);
  }
  void pop(ARMRegister r0, Register r1) {
    vixl::MacroAssembler::Pop(r0, ARMRegister(r1, 64));
  }

  void pop(const ValueOperand& v) { pop(v.valueReg()); }
  void pop(const FloatRegister& f) {
    vixl::MacroAssembler::Pop(ARMFPRegister(f, 64));
  }

  // Update sp with the value of the current active stack pointer, if necessary.
  void syncStackPtr() {
    if (!GetStackPointer64().Is(vixl::sp)) {
      Mov(vixl::sp, GetStackPointer64());
    }
  }
  void initPseudoStackPtr() {
    if (!GetStackPointer64().Is(vixl::sp)) {
      Mov(GetStackPointer64(), vixl::sp);
    }
  }
  // In debug builds only, cause a trap if PSP is active and PSP != SP
  void assertStackPtrsSynced(uint32_t id) {
#ifdef DEBUG
    // The add and sub instructions below will only take a 12-bit immediate.
    MOZ_ASSERT(id <= 0xFFF);
    if (!GetStackPointer64().Is(vixl::sp)) {
      Label ok;
      // Add a marker, so we can figure out who requested the check when
      // inspecting the generated code.  Note, a more concise way to encode
      // the marker would be to use it as an immediate for the `brk`
      // instruction as generated by `Unreachable()`, and removing the add/sub.
      Add(GetStackPointer64(), GetStackPointer64(), Operand(id));
      Sub(GetStackPointer64(), GetStackPointer64(), Operand(id));
      Cmp(vixl::sp, GetStackPointer64());
      B(Equal, &ok);
      Unreachable();
      bind(&ok);
    }
#endif
  }
  // In debug builds only, add a marker that doesn't change the machine's
  // state.  Note these markers are x16-based, as opposed to the x28-based
  // ones made by `assertStackPtrsSynced`.
  void addMarker(uint32_t id) {
#ifdef DEBUG
    // Only 12 bits of immediate are allowed.
    MOZ_ASSERT(id <= 0xFFF);
    ARMRegister x16 = ARMRegister(r16, 64);
    Add(x16, x16, Operand(id));
    Sub(x16, x16, Operand(id));
#endif
  }

  void storeValue(ValueOperand val, const Address& dest) {
    storePtr(val.valueReg(), dest);
  }

  template <typename T>
  void storeValue(JSValueType type, Register reg, const T& dest) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != reg);
    tagValue(type, reg, ValueOperand(scratch));
    storeValue(ValueOperand(scratch), dest);
  }
  template <typename T>
  void storeValue(const Value& val, const T& dest) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    moveValue(val, ValueOperand(scratch));
    storeValue(ValueOperand(scratch), dest);
  }
  void storeValue(ValueOperand val, BaseIndex dest) {
    storePtr(val.valueReg(), dest);
  }
  void storeValue(const Address& src, const Address& dest, Register temp) {
    loadPtr(src, temp);
    storePtr(temp, dest);
  }

  void storePrivateValue(Register src, const Address& dest) {
    storePtr(src, dest);
  }
  void storePrivateValue(ImmGCPtr imm, const Address& dest) {
    storePtr(imm, dest);
  }

  void loadValue(Address src, Register val) {
    Ldr(ARMRegister(val, 64), MemOperand(src));
  }
  void loadValue(Address src, ValueOperand val) {
    Ldr(ARMRegister(val.valueReg(), 64), MemOperand(src));
  }
  void loadValue(const BaseIndex& src, ValueOperand val) {
    doBaseIndex(ARMRegister(val.valueReg(), 64), src, vixl::LDR_x);
  }
  void loadUnalignedValue(const Address& src, ValueOperand dest) {
    loadValue(src, dest);
  }
  void tagValue(JSValueType type, Register payload, ValueOperand dest) {
    // This could be cleverer, but the first attempt had bugs.
    Orr(ARMRegister(dest.valueReg(), 64), ARMRegister(payload, 64),
        Operand(ImmShiftedTag(type).value));
  }
  void pushValue(ValueOperand val) {
    vixl::MacroAssembler::Push(ARMRegister(val.valueReg(), 64));
  }
  void popValue(ValueOperand val) {
    vixl::MacroAssembler::Pop(ARMRegister(val.valueReg(), 64));
    // SP may be < PSP now (that's OK).
    // eg testcase: tests/backup-point-bug1315634.js
  }
  void pushValue(const Value& val) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    if (val.isGCThing()) {
      BufferOffset load =
          movePatchablePtr(ImmPtr(val.bitsAsPunboxPointer()), scratch);
      writeDataRelocation(val, load);
      push(scratch);
    } else {
      moveValue(val, scratch);
      push(scratch);
    }
  }
  void pushValue(JSValueType type, Register reg) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != reg);
    tagValue(type, reg, ValueOperand(scratch));
    push(scratch);
  }
  void pushValue(const Address& addr) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != addr.base);
    loadValue(addr, scratch);
    push(scratch);
  }
  void pushValue(const BaseIndex& addr, Register scratch) {
    loadValue(addr, ValueOperand(scratch));
    pushValue(ValueOperand(scratch));
  }
  template <typename T>
  void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes,
                           JSValueType type) {
    switch (nbytes) {
      case 8: {
        vixl::UseScratchRegisterScope temps(this);
        const Register scratch = temps.AcquireX().asUnsized();
        if (type == JSVAL_TYPE_OBJECT) {
          unboxObjectOrNull(value, scratch);
        } else {
          unboxNonDouble(value, scratch, type);
        }
        storePtr(scratch, address);
        return;
      }
      case 4:
        store32(value.valueReg(), address);
        return;
      case 1:
        store8(value.valueReg(), address);
        return;
      default:
        MOZ_CRASH("Bad payload width");
    }
  }
  void moveValue(const Value& val, Register dest) {
    if (val.isGCThing()) {
      BufferOffset load =
          movePatchablePtr(ImmPtr(val.bitsAsPunboxPointer()), dest);
      writeDataRelocation(val, load);
    } else {
      movePtr(ImmWord(val.asRawBits()), dest);
    }
  }
  void moveValue(const Value& src, const ValueOperand& dest) {
    moveValue(src, dest.valueReg());
  }

  CodeOffset pushWithPatch(ImmWord imm) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    CodeOffset label = movWithPatch(imm, scratch);
    push(scratch);
    return label;
  }

  CodeOffset movWithPatch(ImmWord imm, Register dest) {
    BufferOffset off = immPool64(ARMRegister(dest, 64), imm.value);
    return CodeOffset(off.getOffset());
  }
  CodeOffset movWithPatch(ImmPtr imm, Register dest) {
    BufferOffset off = immPool64(ARMRegister(dest, 64), uint64_t(imm.value));
    return CodeOffset(off.getOffset());
  }

  void boxValue(JSValueType type, Register src, Register dest);

  void splitSignExtTag(Register src, Register dest) {
    sbfx(ARMRegister(dest, 64), ARMRegister(src, 64), JSVAL_TAG_SHIFT,
         (64 - JSVAL_TAG_SHIFT));
  }
  [[nodiscard]] Register extractTag(const Address& address, Register scratch) {
    loadPtr(address, scratch);
    splitSignExtTag(scratch, scratch);
    return scratch;
  }
  [[nodiscard]] Register extractTag(const ValueOperand& value,
                                    Register scratch) {
    splitSignExtTag(value.valueReg(), scratch);
    return scratch;
  }
  [[nodiscard]] Register extractObject(const Address& address,
                                       Register scratch) {
    loadPtr(address, scratch);
    unboxObject(scratch, scratch);
    return scratch;
  }
  [[nodiscard]] Register extractObject(const ValueOperand& value,
                                       Register scratch) {
    unboxObject(value, scratch);
    return scratch;
  }
  [[nodiscard]] Register extractSymbol(const ValueOperand& value,
                                       Register scratch) {
    unboxSymbol(value, scratch);
    return scratch;
  }
  [[nodiscard]] Register extractInt32(const ValueOperand& value,
                                      Register scratch) {
    unboxInt32(value, scratch);
    return scratch;
  }
  [[nodiscard]] Register extractBoolean(const ValueOperand& value,
                                        Register scratch) {
    unboxBoolean(value, scratch);
    return scratch;
  }

  void emitSet(Condition cond, Register dest) {
    Cset(ARMRegister(dest, 64), cond);
  }

  void testNullSet(Condition cond, const ValueOperand& value, Register dest) {
    cond = testNull(cond, value);
    emitSet(cond, dest);
  }
  void testObjectSet(Condition cond, const ValueOperand& value, Register dest) {
    cond = testObject(cond, value);
    emitSet(cond, dest);
  }
  void testUndefinedSet(Condition cond, const ValueOperand& value,
                        Register dest) {
    cond = testUndefined(cond, value);
    emitSet(cond, dest);
  }

  void convertBoolToInt32(Register source, Register dest) {
    Uxtb(ARMRegister(dest, 64), ARMRegister(source, 64));
  }

  void convertInt32ToDouble(Register src, FloatRegister dest) {
    Scvtf(ARMFPRegister(dest, 64),
          ARMRegister(src, 32));  // Uses FPCR rounding mode.
  }
  void convertInt32ToDouble(const Address& src, FloatRegister dest) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != src.base);
    load32(src, scratch);
    convertInt32ToDouble(scratch, dest);
  }
  void convertInt32ToDouble(const BaseIndex& src, FloatRegister dest) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != src.base);
    MOZ_ASSERT(scratch != src.index);
    load32(src, scratch);
    convertInt32ToDouble(scratch, dest);
  }

  void convertInt32ToFloat32(Register src, FloatRegister dest) {
    Scvtf(ARMFPRegister(dest, 32),
          ARMRegister(src, 32));  // Uses FPCR rounding mode.
  }
  void convertInt32ToFloat32(const Address& src, FloatRegister dest) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != src.base);
    load32(src, scratch);
    convertInt32ToFloat32(scratch, dest);
  }

  void convertUInt32ToDouble(Register src, FloatRegister dest) {
    Ucvtf(ARMFPRegister(dest, 64),
          ARMRegister(src, 32));  // Uses FPCR rounding mode.
  }
  void convertUInt32ToDouble(const Address& src, FloatRegister dest) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != src.base);
    load32(src, scratch);
    convertUInt32ToDouble(scratch, dest);
  }

  void convertUInt32ToFloat32(Register src, FloatRegister dest) {
    Ucvtf(ARMFPRegister(dest, 32),
          ARMRegister(src, 32));  // Uses FPCR rounding mode.
  }
  void convertUInt32ToFloat32(const Address& src, FloatRegister dest) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != src.base);
    load32(src, scratch);
    convertUInt32ToFloat32(scratch, dest);
  }

  void convertFloat32ToDouble(FloatRegister src, FloatRegister dest) {
    Fcvt(ARMFPRegister(dest, 64), ARMFPRegister(src, 32));
  }
  void convertDoubleToFloat32(FloatRegister src, FloatRegister dest) {
    Fcvt(ARMFPRegister(dest, 32), ARMFPRegister(src, 64));
  }

  void convertDoubleToFloat16(FloatRegister src, FloatRegister dest) {
    Fcvt(ARMFPRegister(dest, 16), ARMFPRegister(src, 64));
  }
  void convertFloat16ToDouble(FloatRegister src, FloatRegister dest) {
    Fcvt(ARMFPRegister(dest, 64), ARMFPRegister(src, 16));
  }
  void convertFloat32ToFloat16(FloatRegister src, FloatRegister dest) {
    Fcvt(ARMFPRegister(dest, 16), ARMFPRegister(src, 32));
  }
  void convertFloat16ToFloat32(FloatRegister src, FloatRegister dest) {
    Fcvt(ARMFPRegister(dest, 32), ARMFPRegister(src, 16));
  }
  void convertInt32ToFloat16(Register src, FloatRegister dest) {
    // Direct "32-bit to half-precision" move requires (FEAT_FP16), so we
    // instead use a "32-bit to single-precision" move.
    convertInt32ToFloat32(src, dest);
    convertFloat32ToFloat16(dest, dest);
  }

  using vixl::MacroAssembler::B;

  bool hasFjcvtzs() const {
    return CPUHas(vixl::CPUFeatures::kFP, vixl::CPUFeatures::kJSCVT);
  }

  void convertDoubleToInt32(FloatRegister src, Register dest, Label* fail,
                            bool negativeZeroCheck = true) {
    ARMFPRegister fsrc64(src, 64);
    ARMRegister dest32(dest, 32);

    // ARMv8.3 chips support the FJCVTZS instruction, which handles exactly this
    // logic.
    if (hasFjcvtzs()) {
      // Convert double to integer, rounding toward zero.
      // The Z-flag is set iff the conversion is exact. -0 unsets the Z-flag.
      Fjcvtzs(dest32, fsrc64);

      if (negativeZeroCheck) {
        B(fail, Assembler::NonZero);
      } else {
        Label done;
        B(&done, Assembler::Zero);  // If conversion was exact, go to end.

        // The conversion was inexact, but the caller intends to allow -0.

        // Compare fsrc64 to 0.
        // If fsrc64 == 0 and FJCVTZS conversion was inexact, then fsrc64 is -0.
        Fcmp(fsrc64, 0.0);
        B(fail, Assembler::NotEqual);  // Pass through -0; fail otherwise.

        bind(&done);
      }
    } else {
      // Older processors use a significantly slower path.
      ARMRegister dest64(dest, 64);

      vixl::UseScratchRegisterScope temps(this);
      const ARMFPRegister scratch64 = temps.AcquireD();
      MOZ_ASSERT(!scratch64.Is(fsrc64));

      Fcvtzs(dest32, fsrc64);    // Convert, rounding toward zero.
      Scvtf(scratch64, dest32);  // Convert back, using FPCR rounding mode.
      Fcmp(scratch64, fsrc64);
      B(fail, Assembler::NotEqual);

      if (negativeZeroCheck) {
        Label nonzero;
        Cbnz(dest32, &nonzero);
        // dest32 is 0, fail if fsrc64 is negative.
        Fmov(dest64, fsrc64);
        Cmp(dest64, xzr);
        B(fail, Assembler::Signed);
        Mov(dest64, xzr);
        bind(&nonzero);
      }
    }
  }
  void convertFloat32ToInt32(FloatRegister src, Register dest, Label* fail,
                             bool negativeZeroCheck = true) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMFPRegister scratch32 = temps.AcquireS();

    ARMFPRegister fsrc(src, 32);
    ARMRegister dest32(dest, 32);
    ARMRegister dest64(dest, 64);

    MOZ_ASSERT(!scratch32.Is(fsrc));

    Fcvtzs(dest64, fsrc);      // Convert, rounding toward zero.
    Scvtf(scratch32, dest32);  // Convert back, using FPCR rounding mode.
    Fcmp(scratch32, fsrc);
    B(fail, Assembler::NotEqual);

    if (negativeZeroCheck) {
      Label nonzero;
      Cbnz(dest32, &nonzero);
      // dest32 is 0, fail if fsrc64 is negative.
      Fmov(dest32, fsrc);
      Cmp(dest32, xzr);
      B(fail, Assembler::Signed);
      Mov(dest32, xzr);
      bind(&nonzero);
    }
    Uxtw(dest64, dest64);
  }

  void convertDoubleToPtr(FloatRegister src, Register dest, Label* fail,
                          bool negativeZeroCheck = true) {
    ARMFPRegister fsrc64(src, 64);
    ARMRegister dest64(dest, 64);

    vixl::UseScratchRegisterScope temps(this);
    const ARMFPRegister scratch64 = temps.AcquireD();
    MOZ_ASSERT(!scratch64.Is(fsrc64));

    // Note: we can't use the FJCVTZS instruction here because that only works
    // for 32-bit values.

    Fcvtzs(dest64, fsrc64);    // Convert, rounding toward zero.
    Scvtf(scratch64, dest64);  // Convert back, using FPCR rounding mode.
    Fcmp(scratch64, fsrc64);
    B(fail, Assembler::NotEqual);

    if (negativeZeroCheck) {
      Label nonzero;
      Cbnz(dest64, &nonzero);
      // dest64 is 0, fail if fsrc64 is negative.
      Fmov(dest64, fsrc64);
      Cmp(dest64, xzr);
      B(fail, Assembler::Signed);
      Mov(dest64, xzr);
      bind(&nonzero);
    }
  }

  void truncateFloat32ModUint32(FloatRegister src, Register dest) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch64 = temps.AcquireX();

    ARMFPRegister src32(src, 32);
    ARMRegister dest64(dest, 64);

    MOZ_ASSERT(!scratch64.Is(dest64));

    // Convert scalar to signed 64-bit fixed-point, rounding toward zero.
    // In the case of overflow, the output is saturated.
    // In the case of NaN and -0, the output is zero.
    Fcvtzs(dest64, src32);

    // Zero if the result is saturated, i.e. it's either INT64_MIN or INT64_MAX.
    Add(scratch64, dest64, Operand(0x7fff'ffff'ffff'ffff));
    Cmn(scratch64, 3);
    Csel(dest64, dest64, vixl::xzr, Assembler::BelowOrEqual);

    // Clear upper 32 bits.
    Uxtw(dest64, dest64);
  }

  void jump(Label* label) { B(label); }
  void jump(JitCode* code) { branch(code); }
  void jump(ImmPtr ptr) {
    // It is unclear why this sync is necessary:
    // * PSP and SP have been observed to be different in testcase
    //   tests/asm.js/testBug1046688.js.
    // * Removing the sync causes no failures in all of jit-tests.
    //
    // Also see branch(JitCode*) below. This version of jump() is called only
    // from jump(TrampolinePtr) which is called on various very slow paths,
    // probably only in JS.
    syncStackPtr();
    BufferOffset loc =
        b(-1,
          LabelDoc());  // The jump target will be patched by executableCopy().
    addPendingJump(loc, ptr, RelocationKind::HARDCODED);
  }
  void jump(TrampolinePtr code) { jump(ImmPtr(code.value)); }
  void jump(Register reg) { Br(ARMRegister(reg, 64)); }
  void jump(const Address& addr) {
    vixl::UseScratchRegisterScope temps(this);
    MOZ_ASSERT(temps.IsAvailable(ScratchReg64));  // ip0
    temps.Exclude(ScratchReg64);
    MOZ_ASSERT(addr.base != ScratchReg64.asUnsized());
    loadPtr(addr, ScratchReg64.asUnsized());
    br(ScratchReg64);
  }

  void align(int alignment) { armbuffer_.align(alignment); }

  void haltingAlign(int alignment) {
    armbuffer_.align(alignment, vixl::HLT | ImmException(0xBAAD));
  }
  void nopAlign(int alignment) { armbuffer_.align(alignment); }

  void movePtr(Register src, Register dest) {
    Mov(ARMRegister(dest, 64), ARMRegister(src, 64));
  }
  void movePtr(ImmWord imm, Register dest) {
    Mov(ARMRegister(dest, 64), int64_t(imm.value));
  }
  void movePtr(ImmPtr imm, Register dest) {
    Mov(ARMRegister(dest, 64), int64_t(imm.value));
  }
  void movePtr(wasm::SymbolicAddress imm, Register dest) {
    BufferOffset off = movePatchablePtr(ImmWord(0xffffffffffffffffULL), dest);
    append(wasm::SymbolicAccess(CodeOffset(off.getOffset()), imm));
  }
  void movePtr(ImmGCPtr imm, Register dest) {
    BufferOffset load = movePatchablePtr(ImmPtr(imm.value), dest);
    writeDataRelocation(imm, load);
  }

  void mov(ImmWord imm, Register dest) { movePtr(imm, dest); }
  void mov(ImmPtr imm, Register dest) { movePtr(imm, dest); }
  void mov(wasm::SymbolicAddress imm, Register dest) { movePtr(imm, dest); }
  void mov(Register src, Register dest) { movePtr(src, dest); }
  void mov(CodeLabel* label, Register dest);

  void move32(Imm32 imm, Register dest) {
    Mov(ARMRegister(dest, 32), (int64_t)imm.value);
  }
  void move32(Register src, Register dest) {
    Mov(ARMRegister(dest, 32), ARMRegister(src, 32));
  }

  // Move a pointer using a literal pool, so that the pointer
  // may be easily patched or traced.
  // Returns the BufferOffset of the load instruction emitted.
  BufferOffset movePatchablePtr(ImmWord ptr, Register dest);
  BufferOffset movePatchablePtr(ImmPtr ptr, Register dest);

  void loadPtr(wasm::SymbolicAddress address, Register dest) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch = temps.AcquireX();
    movePtr(address, scratch.asUnsized());
    Ldr(ARMRegister(dest, 64), MemOperand(scratch));
  }
  void loadPtr(AbsoluteAddress address, Register dest) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch = temps.AcquireX();
    movePtr(ImmWord((uintptr_t)address.addr), scratch.asUnsized());
    Ldr(ARMRegister(dest, 64), MemOperand(scratch));
  }
  FaultingCodeOffset loadPtr(const Address& address, Register dest) {
    return Ldr(ARMRegister(dest, 64), MemOperand(address));
  }
  FaultingCodeOffset loadPtr(const BaseIndex& src, Register dest) {
    ARMRegister base = toARMRegister(src.base, 64);
    uint32_t scale = Imm32::ShiftOf(src.scale).value;
    ARMRegister dest64(dest, 64);
    ARMRegister index64(src.index, 64);

    if (src.offset) {
      vixl::UseScratchRegisterScope temps(this);
      const ARMRegister scratch = temps.AcquireX();
      MOZ_ASSERT(!scratch.Is(base));
      MOZ_ASSERT(!scratch.Is(dest64));
      MOZ_ASSERT(!scratch.Is(index64));

      Add(scratch, base, Operand(int64_t(src.offset)));
      return Ldr(dest64, MemOperand(scratch, index64, vixl::LSL, scale));
    }

    return Ldr(dest64, MemOperand(base, index64, vixl::LSL, scale));
  }
  void loadPrivate(const Address& src, Register dest);

  FaultingCodeOffset store8(Register src, const Address& address) {
    return Strb(ARMRegister(src, 32), toMemOperand(address));
  }
  void store8(Imm32 imm, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch32 = temps.AcquireW();
    MOZ_ASSERT(scratch32.asUnsized() != address.base);
    move32(imm, scratch32.asUnsized());
    Strb(scratch32, toMemOperand(address));
  }
  FaultingCodeOffset store8(Register src, const BaseIndex& address) {
    return doBaseIndex(ARMRegister(src, 32), address, vixl::STRB_w);
  }
  void store8(Imm32 imm, const BaseIndex& address) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch32 = temps.AcquireW();
    MOZ_ASSERT(scratch32.asUnsized() != address.base);
    MOZ_ASSERT(scratch32.asUnsized() != address.index);
    Mov(scratch32, Operand(imm.value));
    doBaseIndex(scratch32, address, vixl::STRB_w);
  }

  FaultingCodeOffset store16(Register src, const Address& address) {
    return Strh(ARMRegister(src, 32), toMemOperand(address));
  }
  void store16(Imm32 imm, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch32 = temps.AcquireW();
    MOZ_ASSERT(scratch32.asUnsized() != address.base);
    move32(imm, scratch32.asUnsized());
    Strh(scratch32, toMemOperand(address));
  }
  FaultingCodeOffset store16(Register src, const BaseIndex& address) {
    return doBaseIndex(ARMRegister(src, 32), address, vixl::STRH_w);
  }
  void store16(Imm32 imm, const BaseIndex& address) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch32 = temps.AcquireW();
    MOZ_ASSERT(scratch32.asUnsized() != address.base);
    MOZ_ASSERT(scratch32.asUnsized() != address.index);
    Mov(scratch32, Operand(imm.value));
    doBaseIndex(scratch32, address, vixl::STRH_w);
  }
  template <typename S, typename T>
  void store16Unaligned(const S& src, const T& dest) {
    store16(src, dest);
  }

  void storePtr(ImmWord imm, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != address.base);
    movePtr(imm, scratch);
    storePtr(scratch, address);
  }
  void storePtr(ImmPtr imm, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch64 = temps.AcquireX();
    MOZ_ASSERT(scratch64.asUnsized() != address.base);
    Mov(scratch64, uint64_t(imm.value));
    Str(scratch64, toMemOperand(address));
  }
  void storePtr(ImmGCPtr imm, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != address.base);
    movePtr(imm, scratch);
    storePtr(scratch, address);
  }
  FaultingCodeOffset storePtr(Register src, const Address& address) {
    return Str(ARMRegister(src, 64), toMemOperand(address));
  }

  void storePtr(ImmWord imm, const BaseIndex& address) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch64 = temps.AcquireX();
    MOZ_ASSERT(scratch64.asUnsized() != address.base);
    MOZ_ASSERT(scratch64.asUnsized() != address.index);
    Mov(scratch64, Operand(imm.value));
    doBaseIndex(scratch64, address, vixl::STR_x);
  }
  void storePtr(ImmGCPtr imm, const BaseIndex& address) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != address.base);
    MOZ_ASSERT(scratch != address.index);
    movePtr(imm, scratch);
    doBaseIndex(ARMRegister(scratch, 64), address, vixl::STR_x);
  }
  FaultingCodeOffset storePtr(Register src, const BaseIndex& address) {
    return doBaseIndex(ARMRegister(src, 64), address, vixl::STR_x);
  }

  void storePtr(Register src, AbsoluteAddress address) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch64 = temps.AcquireX();
    Mov(scratch64, uint64_t(address.addr));
    Str(ARMRegister(src, 64), MemOperand(scratch64));
  }

  void store32(Register src, AbsoluteAddress address) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch64 = temps.AcquireX();
    Mov(scratch64, uint64_t(address.addr));
    Str(ARMRegister(src, 32), MemOperand(scratch64));
  }
  void store32(Imm32 imm, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch32 = temps.AcquireW();
    MOZ_ASSERT(scratch32.asUnsized() != address.base);
    Mov(scratch32, uint64_t(imm.value));
    Str(scratch32, toMemOperand(address));
  }
  FaultingCodeOffset store32(Register r, const Address& address) {
    return Str(ARMRegister(r, 32), toMemOperand(address));
  }
  void store32(Imm32 imm, const BaseIndex& address) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch32 = temps.AcquireW();
    MOZ_ASSERT(scratch32.asUnsized() != address.base);
    MOZ_ASSERT(scratch32.asUnsized() != address.index);
    Mov(scratch32, imm.value);
    doBaseIndex(scratch32, address, vixl::STR_w);
  }
  FaultingCodeOffset store32(Register r, const BaseIndex& address) {
    return doBaseIndex(ARMRegister(r, 32), address, vixl::STR_w);
  }

  template <typename S, typename T>
  void store32Unaligned(const S& src, const T& dest) {
    store32(src, dest);
  }

  FaultingCodeOffset store64(Register64 src, Address address) {
    return storePtr(src.reg, address);
  }

  FaultingCodeOffset store64(Register64 src, const BaseIndex& address) {
    return storePtr(src.reg, address);
  }

  void store64(Imm64 imm, const BaseIndex& address) {
    storePtr(ImmWord(imm.value), address);
  }

  void store64(Imm64 imm, const Address& address) {
    storePtr(ImmWord(imm.value), address);
  }

  template <typename S, typename T>
  void store64Unaligned(const S& src, const T& dest) {
    store64(src, dest);
  }

  // StackPointer manipulation.
  inline void addToStackPtr(Register src);
  inline void addToStackPtr(Imm32 imm);
  inline void addToStackPtr(const Address& src);
  inline void addStackPtrTo(Register dest);

  inline void subFromStackPtr(Register src);
  inline void subFromStackPtr(Imm32 imm);
  inline void subStackPtrFrom(Register dest);

  inline void andToStackPtr(Imm32 t);

  inline void moveToStackPtr(Register src);
  inline void moveStackPtrTo(Register dest);

  inline void loadStackPtr(const Address& src);
  inline void storeStackPtr(const Address& dest);

  // StackPointer testing functions.
  inline void branchTestStackPtr(Condition cond, Imm32 rhs, Label* label);
  inline void branchStackPtr(Condition cond, Register rhs, Label* label);
  inline void branchStackPtrRhs(Condition cond, Address lhs, Label* label);
  inline void branchStackPtrRhs(Condition cond, AbsoluteAddress lhs,
                                Label* label);

  void testPtr(Register lhs, Register rhs) {
    Tst(ARMRegister(lhs, 64), Operand(ARMRegister(rhs, 64)));
  }
  void test32(Register lhs, Register rhs) {
    Tst(ARMRegister(lhs, 32), Operand(ARMRegister(rhs, 32)));
  }
  void test32(const Address& addr, Imm32 imm) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch32 = temps.AcquireW();
    MOZ_ASSERT(scratch32.asUnsized() != addr.base);
    load32(addr, scratch32.asUnsized());
    Tst(scratch32, Operand(imm.value));
  }
  void test32(Register lhs, Imm32 rhs) {
    Tst(ARMRegister(lhs, 32), Operand(rhs.value));
  }
  void cmp32(Register lhs, Imm32 rhs) {
    Cmp(ARMRegister(lhs, 32), Operand(rhs.value));
  }
  void cmp32(Register a, Register b) {
    Cmp(ARMRegister(a, 32), Operand(ARMRegister(b, 32)));
  }
  void cmp32(const Address& lhs, Imm32 rhs) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch32 = temps.AcquireW();
    MOZ_ASSERT(scratch32.asUnsized() != lhs.base);
    Ldr(scratch32, toMemOperand(lhs));
    Cmp(scratch32, Operand(rhs.value));
  }
  void cmp32(const Address& lhs, Register rhs) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch32 = temps.AcquireW();
    MOZ_ASSERT(scratch32.asUnsized() != lhs.base);
    MOZ_ASSERT(scratch32.asUnsized() != rhs);
    Ldr(scratch32, toMemOperand(lhs));
    Cmp(scratch32, Operand(ARMRegister(rhs, 32)));
  }
  void cmp32(const vixl::Operand& lhs, Imm32 rhs) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch32 = temps.AcquireW();
    Mov(scratch32, lhs);
    Cmp(scratch32, Operand(rhs.value));
  }
  void cmp32(const vixl::Operand& lhs, Register rhs) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch32 = temps.AcquireW();
    Mov(scratch32, lhs);
    Cmp(scratch32, Operand(ARMRegister(rhs, 32)));
  }

  void cmn32(Register lhs, Imm32 rhs) {
    Cmn(ARMRegister(lhs, 32), Operand(rhs.value));
  }

  void cmpPtr(Register lhs, Imm32 rhs) {
    Cmp(ARMRegister(lhs, 64), Operand(rhs.value));
  }
  void cmpPtr(Register lhs, ImmWord rhs) {
    Cmp(ARMRegister(lhs, 64), Operand(rhs.value));
  }
  void cmpPtr(Register lhs, ImmPtr rhs) {
    Cmp(ARMRegister(lhs, 64), Operand(uint64_t(rhs.value)));
  }
  void cmpPtr(Register lhs, Imm64 rhs) {
    Cmp(ARMRegister(lhs, 64), Operand(uint64_t(rhs.value)));
  }
  void cmpPtr(Register lhs, Register rhs) {
    Cmp(ARMRegister(lhs, 64), ARMRegister(rhs, 64));
  }
  void cmpPtr(Register lhs, ImmGCPtr rhs) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != lhs);
    movePtr(rhs, scratch);
    cmpPtr(lhs, scratch);
  }

  void cmpPtr(const Address& lhs, Register rhs) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch64 = temps.AcquireX();
    MOZ_ASSERT(scratch64.asUnsized() != lhs.base);
    MOZ_ASSERT(scratch64.asUnsized() != rhs);
    Ldr(scratch64, toMemOperand(lhs));
    Cmp(scratch64, Operand(ARMRegister(rhs, 64)));
  }
  void cmpPtr(const Address& lhs, ImmWord rhs) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch64 = temps.AcquireX();
    MOZ_ASSERT(scratch64.asUnsized() != lhs.base);
    Ldr(scratch64, toMemOperand(lhs));
    Cmp(scratch64, Operand(rhs.value));
  }
  void cmpPtr(const Address& lhs, ImmPtr rhs) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch64 = temps.AcquireX();
    MOZ_ASSERT(scratch64.asUnsized() != lhs.base);
    Ldr(scratch64, toMemOperand(lhs));
    Cmp(scratch64, Operand(uint64_t(rhs.value)));
  }
  void cmpPtr(const Address& lhs, ImmGCPtr rhs) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != lhs.base);
    loadPtr(lhs, scratch);
    cmpPtr(scratch, rhs);
  }

  FaultingCodeOffset loadDouble(const Address& src, FloatRegister dest) {
    return Ldr(ARMFPRegister(dest, 64), MemOperand(src));
  }
  FaultingCodeOffset loadDouble(const BaseIndex& src, FloatRegister dest) {
    ARMRegister base = toARMRegister(src.base, 64);
    ARMRegister index(src.index, 64);

    if (src.offset == 0) {
      return Ldr(ARMFPRegister(dest, 64),
                 MemOperand(base, index, vixl::LSL, unsigned(src.scale)));
    }

    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch64 = temps.AcquireX();
    MOZ_ASSERT(scratch64.asUnsized() != src.base);
    MOZ_ASSERT(scratch64.asUnsized() != src.index);

    Add(scratch64, base, Operand(index, vixl::LSL, unsigned(src.scale)));
    return Ldr(ARMFPRegister(dest, 64), MemOperand(scratch64, src.offset));
  }

  FaultingCodeOffset loadFloat32(const Address& addr, FloatRegister dest) {
    return Ldr(ARMFPRegister(dest, 32), toMemOperand(addr));
  }
  FaultingCodeOffset loadFloat32(const BaseIndex& src, FloatRegister dest) {
    ARMRegister base = toARMRegister(src.base, 64);
    ARMRegister index(src.index, 64);
    if (src.offset == 0) {
      return Ldr(ARMFPRegister(dest, 32),
                 MemOperand(base, index, vixl::LSL, unsigned(src.scale)));
    } else {
      vixl::UseScratchRegisterScope temps(this);
      const ARMRegister scratch64 = temps.AcquireX();
      MOZ_ASSERT(scratch64.asUnsized() != src.base);
      MOZ_ASSERT(scratch64.asUnsized() != src.index);

      Add(scratch64, base, Operand(index, vixl::LSL, unsigned(src.scale)));
      return Ldr(ARMFPRegister(dest, 32), MemOperand(scratch64, src.offset));
    }
  }

  FaultingCodeOffset loadFloat16(const Address& addr, FloatRegister dest,
                                 Register) {
    return Ldr(ARMFPRegister(dest, 16), toMemOperand(addr));
  }

  FaultingCodeOffset loadFloat16(const BaseIndex& src, FloatRegister dest,
                                 Register) {
    ARMRegister base = toARMRegister(src.base, 64);
    ARMRegister index(src.index, 64);
    if (src.offset == 0) {
      return Ldr(ARMFPRegister(dest, 16),
                 MemOperand(base, index, vixl::LSL, unsigned(src.scale)));
    } else {
      vixl::UseScratchRegisterScope temps(this);
      const ARMRegister scratch64 = temps.AcquireX();
      MOZ_ASSERT(scratch64.asUnsized() != src.base);
      MOZ_ASSERT(scratch64.asUnsized() != src.index);

      Add(scratch64, base, Operand(index, vixl::LSL, unsigned(src.scale)));
      return Ldr(ARMFPRegister(dest, 16), MemOperand(scratch64, src.offset));
    }
  }

  void moveDouble(FloatRegister src, FloatRegister dest) {
    fmov(ARMFPRegister(dest, 64), ARMFPRegister(src, 64));
  }
  void zeroDouble(FloatRegister reg) {
    fmov(ARMFPRegister(reg, 64), vixl::xzr);
  }
  void zeroFloat32(FloatRegister reg) {
    fmov(ARMFPRegister(reg, 32), vixl::wzr);
  }

  void moveFloat32(FloatRegister src, FloatRegister dest) {
    fmov(ARMFPRegister(dest, 32), ARMFPRegister(src, 32));
  }

  void moveSimd128(FloatRegister src, FloatRegister dest) {
    fmov(ARMFPRegister(dest, 128), ARMFPRegister(src, 128));
  }

  void splitSignExtTag(const ValueOperand& operand, Register dest) {
    splitSignExtTag(operand.valueReg(), dest);
  }
  void splitSignExtTag(const Address& operand, Register dest) {
    loadPtr(operand, dest);
    splitSignExtTag(dest, dest);
  }
  void splitSignExtTag(const BaseIndex& operand, Register dest) {
    loadPtr(operand, dest);
    splitSignExtTag(dest, dest);
  }

  // Extracts the tag of a value and places it in tag
  inline void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag);
  void cmpTag(const ValueOperand& operand, ImmTag tag) { MOZ_CRASH("cmpTag"); }

  FaultingCodeOffset load32(const Address& address, Register dest) {
    return Ldr(ARMRegister(dest, 32), toMemOperand(address));
  }
  FaultingCodeOffset load32(const BaseIndex& src, Register dest) {
    return doBaseIndex(ARMRegister(dest, 32), src, vixl::LDR_w);
  }
  void load32(AbsoluteAddress address, Register dest) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch64 = temps.AcquireX();
    movePtr(ImmWord((uintptr_t)address.addr), scratch64.asUnsized());
    ldr(ARMRegister(dest, 32), MemOperand(scratch64));
  }
  template <typename S>
  void load32Unaligned(const S& src, Register dest) {
    load32(src, dest);
  }
  FaultingCodeOffset load64(const Address& address, Register64 dest) {
    return loadPtr(address, dest.reg);
  }
  FaultingCodeOffset load64(const BaseIndex& address, Register64 dest) {
    return loadPtr(address, dest.reg);
  }
  template <typename S>
  void load64Unaligned(const S& src, Register64 dest) {
    load64(src, dest);
  }

  FaultingCodeOffset load8SignExtend(const Address& address, Register dest) {
    return Ldrsb(ARMRegister(dest, 32), toMemOperand(address));
  }
  FaultingCodeOffset load8SignExtend(const BaseIndex& src, Register dest) {
    return doBaseIndex(ARMRegister(dest, 32), src, vixl::LDRSB_w);
  }

  FaultingCodeOffset load8ZeroExtend(const Address& address, Register dest) {
    return Ldrb(ARMRegister(dest, 32), toMemOperand(address));
  }
  FaultingCodeOffset load8ZeroExtend(const BaseIndex& src, Register dest) {
    return doBaseIndex(ARMRegister(dest, 32), src, vixl::LDRB_w);
  }

  FaultingCodeOffset load16SignExtend(const Address& address, Register dest) {
    return Ldrsh(ARMRegister(dest, 32), toMemOperand(address));
  }
  FaultingCodeOffset load16SignExtend(const BaseIndex& src, Register dest) {
    return doBaseIndex(ARMRegister(dest, 32), src, vixl::LDRSH_w);
  }
  template <typename S>
  void load16UnalignedSignExtend(const S& src, Register dest) {
    load16SignExtend(src, dest);
  }

  FaultingCodeOffset load16ZeroExtend(const Address& address, Register dest) {
    return Ldrh(ARMRegister(dest, 32), toMemOperand(address));
  }
  FaultingCodeOffset load16ZeroExtend(const BaseIndex& src, Register dest) {
    return doBaseIndex(ARMRegister(dest, 32), src, vixl::LDRH_w);
  }
  template <typename S>
  void load16UnalignedZeroExtend(const S& src, Register dest) {
    load16ZeroExtend(src, dest);
  }

  void adds32(Register src, Register dest) {
    Adds(ARMRegister(dest, 32), ARMRegister(dest, 32),
         Operand(ARMRegister(src, 32)));
  }
  void adds32(Imm32 imm, Register dest) {
    Adds(ARMRegister(dest, 32), ARMRegister(dest, 32), Operand(imm.value));
  }
  void adds32(Imm32 imm, const Address& dest) {
    vixl::UseScratchRegisterScope temps(this);
    const ARMRegister scratch32 = temps.AcquireW();
    MOZ_ASSERT(scratch32.asUnsized() != dest.base);

    Ldr(scratch32, toMemOperand(dest));
    Adds(scratch32, scratch32, Operand(imm.value));
    Str(scratch32, toMemOperand(dest));
  }
  void adds64(Imm32 imm, Register dest) {
    Adds(ARMRegister(dest, 64), ARMRegister(dest, 64), Operand(imm.value));
  }
  void adds64(ImmWord imm, Register dest) {
    Adds(ARMRegister(dest, 64), ARMRegister(dest, 64), Operand(imm.value));
  }
  void adds64(Register src, Register dest) {
    Adds(ARMRegister(dest, 64), ARMRegister(dest, 64),
         Operand(ARMRegister(src, 64)));
  }

  void subs32(Imm32 imm, Register dest) {
    Subs(ARMRegister(dest, 32), ARMRegister(dest, 32), Operand(imm.value));
  }
  void subs32(Register src, Register dest) {
    Subs(ARMRegister(dest, 32), ARMRegister(dest, 32),
         Operand(ARMRegister(src, 32)));
  }
  void subs64(Imm32 imm, Register dest) {
    Subs(ARMRegister(dest, 64), ARMRegister(dest, 64), Operand(imm.value));
  }
  void subs64(Register src, Register dest) {
    Subs(ARMRegister(dest, 64), ARMRegister(dest, 64),
         Operand(ARMRegister(src, 64)));
  }

  void negs32(Register reg) {
    Negs(ARMRegister(reg, 32), Operand(ARMRegister(reg, 32)));
  }
  void negs64(Register reg) {
    Negs(ARMRegister(reg, 64), Operand(ARMRegister(reg, 64)));
  }

  void ret() {
    pop(lr);
    abiret();
  }

  void retn(Imm32 n) {
    vixl::UseScratchRegisterScope temps(this);
    MOZ_ASSERT(temps.IsAvailable(ScratchReg64));  // ip0
    temps.Exclude(ScratchReg64);
    // ip0 <- [sp]; sp += n; ret ip0
    Ldr(ScratchReg64,
        MemOperand(GetStackPointer64(), ptrdiff_t(n.value), vixl::PostIndex));
    syncStackPtr();  // SP is always used to transmit the stack between calls.
    Ret(ScratchReg64);
  }

  void j(Condition cond, Label* dest) { B(dest, cond); }

  void branch(Condition cond, Label* label) { B(label, cond); }
  void branch(JitCode* target) {
    // It is unclear why this sync is necessary:
    // * PSP and SP have been observed to be different in testcase
    //   tests/async/debugger-reject-after-fulfill.js
    // * Removing the sync causes no failures in all of jit-tests.
    //
    // Also see jump() above.  This is used only to implement jump(JitCode*)
    // and only for JS, it appears.
    syncStackPtr();
    BufferOffset loc =
        b(-1,
          LabelDoc());  // The jump target will be patched by executableCopy().
    addPendingJump(loc, ImmPtr(target->raw()), RelocationKind::JITCODE);
  }

  void compareDouble(FloatRegister lhs, FloatRegister rhs) {
    Fcmp(ARMFPRegister(lhs, 64), ARMFPRegister(rhs, 64));
  }

  void compareFloat(FloatRegister lhs, FloatRegister rhs) {
    Fcmp(ARMFPRegister(lhs, 32), ARMFPRegister(rhs, 32));
  }

  void compareSimd128Int(Assembler::Condition cond, ARMFPRegister dest,
                         ARMFPRegister lhs, ARMFPRegister rhs);
  void compareSimd128Float(Assembler::Condition cond, ARMFPRegister dest,
                           ARMFPRegister lhs, ARMFPRegister rhs);
  void rightShiftInt8x16(FloatRegister lhs, Register rhs, FloatRegister dest,
                         bool isUnsigned);
  void rightShiftInt16x8(FloatRegister lhs, Register rhs, FloatRegister dest,
                         bool isUnsigned);
  void rightShiftInt32x4(FloatRegister lhs, Register rhs, FloatRegister dest,
                         bool isUnsigned);
  void rightShiftInt64x2(FloatRegister lhs, Register rhs, FloatRegister dest,
                         bool isUnsigned);

  void boxDouble(FloatRegister src, const ValueOperand& dest, FloatRegister) {
    Fmov(ARMRegister(dest.valueReg(), 64), ARMFPRegister(src, 64));
  }
  void boxNonDouble(JSValueType type, Register src, const ValueOperand& dest) {
    boxValue(type, src, dest.valueReg());
  }

  // Note that the |dest| register here may be ScratchReg, so we shouldn't use
  // it.
  void unboxInt32(const ValueOperand& src, Register dest) {
    move32(src.valueReg(), dest);
  }
  void unboxInt32(const Address& src, Register dest) { load32(src, dest); }
  void unboxInt32(const BaseIndex& src, Register dest) { load32(src, dest); }

  template <typename T>
  void unboxDouble(const T& src, FloatRegister dest) {
    loadDouble(src, dest);
  }
  void unboxDouble(const ValueOperand& src, FloatRegister dest) {
    Fmov(ARMFPRegister(dest, 64), ARMRegister(src.valueReg(), 64));
  }

  void unboxArgObjMagic(const ValueOperand& src, Register dest) {
    MOZ_CRASH("unboxArgObjMagic");
  }
  void unboxArgObjMagic(const Address& src, Register dest) {
    MOZ_CRASH("unboxArgObjMagic");
  }

  void unboxBoolean(const ValueOperand& src, Register dest) {
    move32(src.valueReg(), dest);
  }
  void unboxBoolean(const Address& src, Register dest) { load32(src, dest); }
  void unboxBoolean(const BaseIndex& src, Register dest) { load32(src, dest); }

  void unboxMagic(const ValueOperand& src, Register dest) {
    move32(src.valueReg(), dest);
  }
  void unboxNonDouble(const ValueOperand& src, Register dest,
                      JSValueType type) {
    unboxNonDouble(src.valueReg(), dest, type);
  }

  template <typename T>
  void unboxNonDouble(T src, Register dest, JSValueType type) {
    MOZ_ASSERT(type != JSVAL_TYPE_DOUBLE);
    if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
      load32(src, dest);
      return;
    }
    loadPtr(src, dest);
    unboxNonDouble(dest, dest, type);
  }

  void unboxNonDouble(Register src, Register dest, JSValueType type) {
    MOZ_ASSERT(type != JSVAL_TYPE_DOUBLE);
    if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
      move32(src, dest);
      return;
    }
    Eor(ARMRegister(dest, 64), ARMRegister(src, 64),
        Operand(JSVAL_TYPE_TO_SHIFTED_TAG(type)));
  }

  void notBoolean(const ValueOperand& val) {
    ARMRegister r(val.valueReg(), 64);
    eor(r, r, Operand(1));
  }
  void unboxObject(const ValueOperand& src, Register dest) {
    unboxNonDouble(src.valueReg(), dest, JSVAL_TYPE_OBJECT);
  }
  void unboxObject(Register src, Register dest) {
    unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
  }
  void unboxObject(const Address& src, Register dest) {
    loadPtr(src, dest);
    unboxNonDouble(dest, dest, JSVAL_TYPE_OBJECT);
  }
  void unboxObject(const BaseIndex& src, Register dest) {
    doBaseIndex(ARMRegister(dest, 64), src, vixl::LDR_x);
    unboxNonDouble(dest, dest, JSVAL_TYPE_OBJECT);
  }

  template <typename T>
  void unboxObjectOrNull(const T& src, Register dest) {
    unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
    And(ARMRegister(dest, 64), ARMRegister(dest, 64),
        Operand(~JS::detail::ValueObjectOrNullBit));
  }

  // See comment in MacroAssembler-x64.h.
  void unboxGCThingForGCBarrier(const Address& src, Register dest) {
    loadPtr(src, dest);
    And(ARMRegister(dest, 64), ARMRegister(dest, 64),
        Operand(JS::detail::ValueGCThingPayloadMask));
  }
  void unboxGCThingForGCBarrier(const ValueOperand& src, Register dest) {
    And(ARMRegister(dest, 64), ARMRegister(src.valueReg(), 64),
        Operand(JS::detail::ValueGCThingPayloadMask));
  }

  void unboxWasmAnyRefGCThingForGCBarrier(const Address& src, Register dest) {
    loadPtr(src, dest);
    And(ARMRegister(dest, 64), ARMRegister(dest, 64),
        Operand(wasm::AnyRef::GCThingMask));
  }

  // Like unboxGCThingForGCBarrier, but loads the GC thing's chunk base.
  void getGCThingValueChunk(Register src, Register dest) {
    And(ARMRegister(src, 64), ARMRegister(dest, 64),
        Operand(JS::detail::ValueGCThingPayloadChunkMask));
  }
  void getGCThingValueChunk(const Address& src, Register dest) {
    loadPtr(src, dest);
    And(ARMRegister(dest, 64), ARMRegister(dest, 64),
        Operand(JS::detail::ValueGCThingPayloadChunkMask));
  }
  void getGCThingValueChunk(const ValueOperand& src, Register dest) {
    And(ARMRegister(dest, 64), ARMRegister(src.valueReg(), 64),
        Operand(JS::detail::ValueGCThingPayloadChunkMask));
  }

  void getWasmAnyRefGCThingChunk(Register src, Register dest) {
    And(ARMRegister(dest, 64), ARMRegister(src, 64),
        Operand(wasm::AnyRef::GCThingChunkMask));
  }

  inline void unboxValue(const ValueOperand& src, AnyRegister dest,
                         JSValueType type);

  void unboxString(const ValueOperand& operand, Register dest) {
    unboxNonDouble(operand, dest, JSVAL_TYPE_STRING);
  }
  void unboxString(const Address& src, Register dest) {
    unboxNonDouble(src, dest, JSVAL_TYPE_STRING);
  }
  void unboxSymbol(const ValueOperand& operand, Register dest) {
    unboxNonDouble(operand, dest, JSVAL_TYPE_SYMBOL);
  }
  void unboxSymbol(const Address& src, Register dest) {
    unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL);
  }
  void unboxBigInt(const ValueOperand& operand, Register dest) {
    unboxNonDouble(operand, dest, JSVAL_TYPE_BIGINT);
  }
  void unboxBigInt(const Address& src, Register dest) {
    unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
  }

  void loadConstantDouble(double d, FloatRegister dest) {
    ARMFPRegister r(dest, 64);
    if (d == 0.0) {
      // Clang11 does movi for 0 and movi+fneg for -0, and this seems like a
      // good implementation-independent strategy as it avoids any gpr->fpr
      // moves or memory traffic.
      Movi(r, 0);
      if (std::signbit(d)) {
        Fneg(r, r);
      }
    } else {
      Fmov(r, d);
    }
  }
  void loadConstantFloat32(float f, FloatRegister dest) {
    ARMFPRegister r(dest, 32);
    if (f == 0.0) {
      // See comments above.  There's not a movi variant for a single register,
      // so clear the double.
      Movi(ARMFPRegister(dest, 64), 0);
      if (std::signbit(f)) {
        Fneg(r, r);
      }
    } else {
      Fmov(r, f);
    }
  }

  void cmpTag(Register tag, ImmTag ref) {
    // As opposed to other architecture, splitTag is replaced by splitSignExtTag
    // which extract the tag with a sign extension. The reason being that cmp32
    // with a tag value would be too large to fit as a 12 bits immediate value,
    // and would require the VIXL macro assembler to add an extra instruction
    // and require extra scratch register to load the Tag value.
    //
    // Instead, we compare with the negative value of the sign extended tag with
    // the CMN instruction. The sign extended tag is expected to be a negative
    // value. Therefore the negative of the sign extended tag is expected to be
    // near 0 and fit on 12 bits.
    //
    // Ignoring the sign extension, the logic is the following:
    //
    //   CMP32(Reg, Tag) = Reg - Tag
    //                   = Reg + (-Tag)
    //                   = CMN32(Reg, -Tag)
    //
    // Note: testGCThing, testPrimitive and testNumber which are checking for
    // inequalities should use unsigned comparisons (as done by default) in
    // order to keep the same relation order after the sign extension, i.e.
    // using Above or Below which are based on the carry flag.
    uint32_t hiShift = JSVAL_TAG_SHIFT - 32;
    int32_t seTag = int32_t(ref.value);
    seTag = (seTag << hiShift) >> hiShift;
    MOZ_ASSERT(seTag < 0);
    int32_t negTag = -seTag;
    // Check thest negTag is encoded on a 12 bits immediate value.
    MOZ_ASSERT((negTag & ~0xFFF) == 0);
    cmn32(tag, Imm32(negTag));
  }

  // Register-based tests.
  Condition testUndefined(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JSVAL_TAG_UNDEFINED));
    return cond;
  }
  Condition testInt32(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JSVAL_TAG_INT32));
    return cond;
  }
  Condition testBoolean(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JSVAL_TAG_BOOLEAN));
    return cond;
  }
  Condition testNull(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JSVAL_TAG_NULL));
    return cond;
  }
  Condition testString(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JSVAL_TAG_STRING));
    return cond;
  }
  Condition testSymbol(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JSVAL_TAG_SYMBOL));
    return cond;
  }
  Condition testBigInt(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JSVAL_TAG_BIGINT));
    return cond;
  }
  Condition testObject(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JSVAL_TAG_OBJECT));
    return cond;
  }
  Condition testDouble(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JSVAL_TAG_MAX_DOUBLE));
    // Requires unsigned comparison due to cmpTag internals.
    return (cond == Equal) ? BelowOrEqual : Above;
  }
  Condition testNumber(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JS::detail::ValueUpperInclNumberTag));
    // Requires unsigned comparison due to cmpTag internals.
    return (cond == Equal) ? BelowOrEqual : Above;
  }
  Condition testGCThing(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JS::detail::ValueLowerInclGCThingTag));
    // Requires unsigned comparison due to cmpTag internals.
    return (cond == Equal) ? AboveOrEqual : Below;
  }
  Condition testMagic(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JSVAL_TAG_MAGIC));
    return cond;
  }
  Condition testPrimitive(Condition cond, Register tag) {
    MOZ_ASSERT(cond == Equal || cond == NotEqual);
    cmpTag(tag, ImmTag(JS::detail::ValueUpperExclPrimitiveTag));
    // Requires unsigned comparison due to cmpTag internals.
    return (cond == Equal) ? Below : AboveOrEqual;
  }
  Condition testError(Condition cond, Register tag) {
    return testMagic(cond, tag);
  }

  // ValueOperand-based tests.
  Condition testInt32(Condition cond, const ValueOperand& value) {
    // The incoming ValueOperand may use scratch registers.
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(scratch != value.valueReg());

    splitSignExtTag(value, scratch);
    return testInt32(cond, scratch);
  }
  Condition testBoolean(Condition cond, const ValueOperand& value) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(value.valueReg() != scratch);
    splitSignExtTag(value, scratch);
    return testBoolean(cond, scratch);
  }
  Condition testDouble(Condition cond, const ValueOperand& value) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(value.valueReg() != scratch);
    splitSignExtTag(value, scratch);
    return testDouble(cond, scratch);
  }
  Condition testNull(Condition cond, const ValueOperand& value) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(value.valueReg() != scratch);
    splitSignExtTag(value, scratch);
    return testNull(cond, scratch);
  }
  Condition testUndefined(Condition cond, const ValueOperand& value) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(value.valueReg() != scratch);
    splitSignExtTag(value, scratch);
    return testUndefined(cond, scratch);
  }
  Condition testString(Condition cond, const ValueOperand& value) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(value.valueReg() != scratch);
    splitSignExtTag(value, scratch);
    return testString(cond, scratch);
  }
  Condition testSymbol(Condition cond, const ValueOperand& value) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(value.valueReg() != scratch);
    splitSignExtTag(value, scratch);
    return testSymbol(cond, scratch);
  }
  Condition testBigInt(Condition cond, const ValueOperand& value) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(value.valueReg() != scratch);
    splitSignExtTag(value, scratch);
    return testBigInt(cond, scratch);
  }
  Condition testObject(Condition cond, const ValueOperand& value) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(value.valueReg() != scratch);
    splitSignExtTag(value, scratch);
    return testObject(cond, scratch);
  }
  Condition testNumber(Condition cond, const ValueOperand& value) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(value.valueReg() != scratch);
    splitSignExtTag(value, scratch);
    return testNumber(cond, scratch);
  }
  Condition testPrimitive(Condition cond, const ValueOperand& value) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(value.valueReg() != scratch);
    splitSignExtTag(value, scratch);
    return testPrimitive(cond, scratch);
  }
  Condition testMagic(Condition cond, const ValueOperand& value) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(value.valueReg() != scratch);
    splitSignExtTag(value, scratch);
    return testMagic(cond, scratch);
  }
  Condition testGCThing(Condition cond, const ValueOperand& value) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(value.valueReg() != scratch);
    splitSignExtTag(value, scratch);
    return testGCThing(cond, scratch);
  }
  Condition testError(Condition cond, const ValueOperand& value) {
    return testMagic(cond, value);
  }

  // Address-based tests.
  Condition testGCThing(Condition cond, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(address.base != scratch);
    splitSignExtTag(address, scratch);
    return testGCThing(cond, scratch);
  }
  Condition testMagic(Condition cond, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(address.base != scratch);
    splitSignExtTag(address, scratch);
    return testMagic(cond, scratch);
  }
  Condition testInt32(Condition cond, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(address.base != scratch);
    splitSignExtTag(address, scratch);
    return testInt32(cond, scratch);
  }
  Condition testDouble(Condition cond, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(address.base != scratch);
    splitSignExtTag(address, scratch);
    return testDouble(cond, scratch);
  }
  Condition testBoolean(Condition cond, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(address.base != scratch);
    splitSignExtTag(address, scratch);
    return testBoolean(cond, scratch);
  }
  Condition testNull(Condition cond, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(address.base != scratch);
    splitSignExtTag(address, scratch);
    return testNull(cond, scratch);
  }
  Condition testUndefined(Condition cond, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(address.base != scratch);
    splitSignExtTag(address, scratch);
    return testUndefined(cond, scratch);
  }
  Condition testString(Condition cond, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(address.base != scratch);
    splitSignExtTag(address, scratch);
    return testString(cond, scratch);
  }
  Condition testSymbol(Condition cond, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
    const Register scratch = temps.AcquireX().asUnsized();
    MOZ_ASSERT(address.base != scratch);
    splitSignExtTag(address, scratch);
    return testSymbol(cond, scratch);
  }
  Condition testBigInt(Condition cond, const Address& address) {
    vixl::UseScratchRegisterScope temps(this);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=97 G=95

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.