// Copyright 2015, VIXL authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of ARM Limited nor the names of its contributors may be
//     used to endorse or promote products derived from this software without
//     specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef VIXL_A64_MACRO_ASSEMBLER_A64_H_
#define VIXL_A64_MACRO_ASSEMBLER_A64_H_

#include <algorithm>
#include <limits>

#include "jit/arm64/Assembler-arm64.h"
#include "jit/arm64/vixl/Debugger-vixl.h"
#include "jit/arm64/vixl/Globals-vixl.h"
#include "jit/arm64/vixl/Instrument-vixl.h"
#include "jit/arm64/vixl/Simulator-Constants-vixl.h"

#define LS_MACRO_LIST(V)                                      \
  V(Ldrb, Register&, rt, LDRB_w)                              \
  V(Strb, Register&, rt, STRB_w)                              \
  V(Ldrsb, Register&, rt, rt.Is64Bits() ? LDRSB_x : LDRSB_w)  \
  V(Ldrh, Register&, rt, LDRH_w)                              \
  V(Strh, Register&, rt, STRH_w)                              \
  V(Ldrsh, Register&, rt, rt.Is64Bits() ? LDRSH_x : LDRSH_w)  \
  V(Ldr, CPURegister&, rt, LoadOpFor(rt))                     \
  V(Str, CPURegister&, rt, StoreOpFor(rt))                    \
  V(Ldrsw, Register&, rt, LDRSW_x)


#define LSPAIR_MACRO_LIST(V)                              \
  V(Ldp, CPURegister&, rt, rt2, LoadPairOpFor(rt, rt2))   \
  V(Stp, CPURegister&, rt, rt2, StorePairOpFor(rt, rt2))  \
  V(Ldpsw, CPURegister&, rt, rt2, LDPSW_x)

namespace vixl {

// Forward declaration
class MacroAssembler;
class UseScratchRegisterScope;

// This scope has the following purposes:
//  * Acquire/Release the underlying assembler's code buffer.
//     * This is mandatory before emitting.
//  * Emit the literal or veneer pools if necessary before emitting the
//    macro-instruction.
//  * Ensure there is enough space to emit the macro-instruction.
class EmissionCheckScope {
 public:
  EmissionCheckScope(MacroAssembler* masm, size_t size)
    : masm_(masm)
  { }

 protected:
  MacroAssembler* masm_;
#ifdef DEBUG
  Label start_;
  size_t size_;
#endif
};


// Helper for common Emission checks.
// The macro-instruction maps to a single instruction.
class SingleEmissionCheckScope : public EmissionCheckScope {
 public:
  explicit SingleEmissionCheckScope(MacroAssembler* masm)
      : EmissionCheckScope(masm, kInstructionSize) {}
};


// The macro instruction is a "typical" macro-instruction. Typical macro-
// instruction only emit a few instructions, a few being defined as 8 here.
class MacroEmissionCheckScope : public EmissionCheckScope {
 public:
  explicit MacroEmissionCheckScope(MacroAssembler* masm)
      : EmissionCheckScope(masm, kTypicalMacroInstructionMaxSize) {}

 private:
  static const size_t kTypicalMacroInstructionMaxSize = 8 * kInstructionSize;
};


enum BranchType {
  // Copies of architectural conditions.
  // The associated conditions can be used in place of those, the code will
  // take care of reinterpreting them with the correct type.
  integer_eq = eq,
  integer_ne = ne,
  integer_hs = hs,
  integer_lo = lo,
  integer_mi = mi,
  integer_pl = pl,
  integer_vs = vs,
  integer_vc = vc,
  integer_hi = hi,
  integer_ls = ls,
  integer_ge = ge,
  integer_lt = lt,
  integer_gt = gt,
  integer_le = le,
  integer_al = al,
  integer_nv = nv,

  // These two are *different* from the architectural codes al and nv.
  // 'always' is used to generate unconditional branches.
  // 'never' is used to not generate a branch (generally as the inverse
  // branch type of 'always).
  always, never,
  // cbz and cbnz
  reg_zero, reg_not_zero,
  // tbz and tbnz
  reg_bit_clear, reg_bit_set,

  // Aliases.
  kBranchTypeFirstCondition = eq,
  kBranchTypeLastCondition = nv,
  kBranchTypeFirstUsingReg = reg_zero,
  kBranchTypeFirstUsingBit = reg_bit_clear
};


enum DiscardMoveMode { kDontDiscardForSameWReg, kDiscardForSameWReg };

// The macro assembler supports moving automatically pre-shifted immediates for
// arithmetic and logical instructions, and then applying a post shift in the
// instruction to undo the modification, in order to reduce the code emitted for
// an operation. For example:
//
//  Add(x0, x0, 0x1f7de) => movz x16, 0xfbef; add x0, x0, x16, lsl #1.
//
// This optimisation can be only partially applied when the stack pointer is an
// operand or destination, so this enumeration is used to control the shift.
enum PreShiftImmMode {
  kNoShift,          // Don't pre-shift.
  kLimitShiftForSP,  // Limit pre-shift for add/sub extend use.
  kAnyShift          // Allow any pre-shift.
};


class MacroAssembler : public js::jit::Assembler {
 public:
  MacroAssembler();

  // Finalize a code buffer of generated instructions. This function must be
  // called before executing or copying code from the buffer.
  void FinalizeCode();


  // Constant generation helpers.
  // These functions return the number of instructions required to move the
  // immediate into the destination register. Also, if the masm pointer is
  // non-null, it generates the code to do so.
  // The two features are implemented using one function to avoid duplication of
  // the logic.
  // The function can be used to evaluate the cost of synthesizing an
  // instruction using 'mov immediate' instructions. A user might prefer loading
  // a constant using the literal pool instead of using multiple 'mov immediate'
  // instructions.
  static int MoveImmediateHelper(MacroAssembler* masm,
                                 const Register &rd,
                                 uint64_t imm);
  static bool OneInstrMoveImmediateHelper(MacroAssembler* masm,
                                          const Register& dst,
                                          int64_t imm);


  // Logical macros.
  void And(const Register& rd,
           const Register& rn,
           const Operand& operand);
  void Ands(const Register& rd,
            const Register& rn,
            const Operand& operand);
  void Bic(const Register& rd,
           const Register& rn,
           const Operand& operand);
  void Bics(const Register& rd,
            const Register& rn,
            const Operand& operand);
  void Orr(const Register& rd,
           const Register& rn,
           const Operand& operand);
  void Orn(const Register& rd,
           const Register& rn,
           const Operand& operand);
  void Eor(const Register& rd,
           const Register& rn,
           const Operand& operand);
  void Eon(const Register& rd,
           const Register& rn,
           const Operand& operand);
  void Tst(const Register& rn, const Operand& operand);
  void LogicalMacro(const Register& rd,
                    const Register& rn,
                    const Operand& operand,
                    LogicalOp op);

  // Add and sub macros.
  void Add(const Register& rd,
           const Register& rn,
           const Operand& operand,
           FlagsUpdate S = LeaveFlags);
  void Adds(const Register& rd,
            const Register& rn,
            const Operand& operand);
  void Sub(const Register& rd,
           const Register& rn,
           const Operand& operand,
           FlagsUpdate S = LeaveFlags);
  void Subs(const Register& rd,
            const Register& rn,
            const Operand& operand);
  void Cmn(const Register& rn, const Operand& operand);
  void Cmp(const Register& rn, const Operand& operand);
  void Neg(const Register& rd,
           const Operand& operand);
  void Negs(const Register& rd,
            const Operand& operand);

  void AddSubMacro(const Register& rd,
                   const Register& rn,
                   const Operand& operand,
                   FlagsUpdate S,
                   AddSubOp op);

  // Add/sub with carry macros.
  void Adc(const Register& rd,
           const Register& rn,
           const Operand& operand);
  void Adcs(const Register& rd,
            const Register& rn,
            const Operand& operand);
  void Sbc(const Register& rd,
           const Register& rn,
           const Operand& operand);
  void Sbcs(const Register& rd,
            const Register& rn,
            const Operand& operand);
  void Ngc(const Register& rd,
           const Operand& operand);
  void Ngcs(const Register& rd,
            const Operand& operand);
  void AddSubWithCarryMacro(const Register& rd,
                            const Register& rn,
                            const Operand& operand,
                            FlagsUpdate S,
                            AddSubWithCarryOp op);

  // Move macros.
  void Mov(const Register& rd, uint64_t imm);
  void Mov(const Register& rd,
           const Operand& operand,
           DiscardMoveMode discard_mode = kDontDiscardForSameWReg);
  void Mvn(const Register& rd, uint64_t imm) {
    Mov(rd, (rd.size() == kXRegSize) ? ~imm : (~imm & kWRegMask));
  }
  void Mvn(const Register& rd, const Operand& operand);

  // Try to move an immediate into the destination register in a single
  // instruction. Returns true for success, and updates the contents of dst.
  // Returns false, otherwise.
  bool TryOneInstrMoveImmediate(const Register& dst, int64_t imm);

  // Move an immediate into register dst, and return an Operand object for
  // use with a subsequent instruction that accepts a shift. The value moved
  // into dst is not necessarily equal to imm; it may have had a shifting
  // operation applied to it that will be subsequently undone by the shift
  // applied in the Operand.
  Operand MoveImmediateForShiftedOp(const Register& dst,
		                    int64_t imm,
				    PreShiftImmMode mode);

  // Synthesises the address represented by a MemOperand into a register.
  void ComputeAddress(const Register& dst, const MemOperand& mem_op);

  // Conditional macros.
  void Ccmp(const Register& rn,
            const Operand& operand,
            StatusFlags nzcv,
            Condition cond);
  void Ccmn(const Register& rn,
            const Operand& operand,
            StatusFlags nzcv,
            Condition cond);
  void ConditionalCompareMacro(const Register& rn,
                               const Operand& operand,
                               StatusFlags nzcv,
                               Condition cond,
                               ConditionalCompareOp op);
  void Csel(const Register& rd,
            const Register& rn,
            const Operand& operand,
            Condition cond);

  // Load/store macros.
#define DECLARE_FUNCTION(FN, REGTYPE, REG, OP) \
  void FN(const REGTYPE REG, const MemOperand& addr);
  LS_MACRO_LIST(DECLARE_FUNCTION)
#undef DECLARE_FUNCTION

  void LoadStoreMacro(const CPURegister& rt,
                      const MemOperand& addr,
                      LoadStoreOp op);

#define DECLARE_FUNCTION(FN, REGTYPE, REG, REG2, OP) \
  void FN(const REGTYPE REG, const REGTYPE REG2, const MemOperand& addr);
  LSPAIR_MACRO_LIST(DECLARE_FUNCTION)
#undef DECLARE_FUNCTION

  void LoadStorePairMacro(const CPURegister& rt,
                          const CPURegister& rt2,
                          const MemOperand& addr,
                          LoadStorePairOp op);

  void Prfm(PrefetchOperation op, const MemOperand& addr);

  // Push or pop up to 4 registers of the same width to or from the stack,
  // using the current stack pointer as set by SetStackPointer.
  //
  // If an argument register is 'NoReg', all further arguments are also assumed
  // to be 'NoReg', and are thus not pushed or popped.
  //
  // Arguments are ordered such that "Push(a, b);" is functionally equivalent
  // to "Push(a); Push(b);".
  //
  // It is valid to push the same register more than once, and there is no
  // restriction on the order in which registers are specified.
  //
  // It is not valid to pop into the same register more than once in one
  // operation, not even into the zero register.
  //
  // If the current stack pointer (as set by SetStackPointer) is sp, then it
  // must be aligned to 16 bytes on entry and the total size of the specified
  // registers must also be a multiple of 16 bytes.
  //
  // Even if the current stack pointer is not the system stack pointer (sp),
  // Push (and derived methods) will still modify the system stack pointer in
  // order to comply with ABI rules about accessing memory below the system
  // stack pointer.
  //
  // Other than the registers passed into Pop, the stack pointer and (possibly)
  // the system stack pointer, these methods do not modify any other registers.
  void Push(const CPURegister& src0, const CPURegister& src1 = NoReg,
            const CPURegister& src2 = NoReg, const CPURegister& src3 = NoReg);
  void Pop(const CPURegister& dst0, const CPURegister& dst1 = NoReg,
           const CPURegister& dst2 = NoReg, const CPURegister& dst3 = NoReg);
  void PushStackPointer();

  // Alternative forms of Push and Pop, taking a RegList or CPURegList that
  // specifies the registers that are to be pushed or popped. Higher-numbered
  // registers are associated with higher memory addresses (as in the A32 push
  // and pop instructions).
  //
  // (Push|Pop)SizeRegList allow you to specify the register size as a
  // parameter. Only kXRegSize, kWRegSize, kDRegSize and kSRegSize are
  // supported.
  //
  // Otherwise, (Push|Pop)(CPU|X|W|D|S)RegList is preferred.
  void PushCPURegList(CPURegList registers);
  void PopCPURegList(CPURegList registers);

  void PushSizeRegList(RegList registers, unsigned reg_size,
      CPURegister::RegisterType type = CPURegister::kRegister) {
    PushCPURegList(CPURegList(type, reg_size, registers));
  }
  void PopSizeRegList(RegList registers, unsigned reg_size,
      CPURegister::RegisterType type = CPURegister::kRegister) {
    PopCPURegList(CPURegList(type, reg_size, registers));
  }
  void PushXRegList(RegList regs) {
    PushSizeRegList(regs, kXRegSize);
  }
  void PopXRegList(RegList regs) {
    PopSizeRegList(regs, kXRegSize);
  }
  void PushWRegList(RegList regs) {
    PushSizeRegList(regs, kWRegSize);
  }
  void PopWRegList(RegList regs) {
    PopSizeRegList(regs, kWRegSize);
  }
  void PushDRegList(RegList regs) {
    PushSizeRegList(regs, kDRegSize, CPURegister::kVRegister);
  }
  void PopDRegList(RegList regs) {
    PopSizeRegList(regs, kDRegSize, CPURegister::kVRegister);
  }
  void PushSRegList(RegList regs) {
    PushSizeRegList(regs, kSRegSize, CPURegister::kVRegister);
  }
  void PopSRegList(RegList regs) {
    PopSizeRegList(regs, kSRegSize, CPURegister::kVRegister);
  }

  // Push the specified register 'count' times.
  void PushMultipleTimes(int count, Register src);

  // Poke 'src' onto the stack. The offset is in bytes.
  //
  // If the current stack pointer (as set by SetStackPointer) is sp, then sp
  // must be aligned to 16 bytes.
  void Poke(const Register& src, const Operand& offset);

  // Peek at a value on the stack, and put it in 'dst'. The offset is in bytes.
  //
  // If the current stack pointer (as set by SetStackPointer) is sp, then sp
  // must be aligned to 16 bytes.
  void Peek(const Register& dst, const Operand& offset);

  // Alternative forms of Peek and Poke, taking a RegList or CPURegList that
  // specifies the registers that are to be pushed or popped. Higher-numbered
  // registers are associated with higher memory addresses.
  //
  // (Peek|Poke)SizeRegList allow you to specify the register size as a
  // parameter. Only kXRegSize, kWRegSize, kDRegSize and kSRegSize are
  // supported.
  //
  // Otherwise, (Peek|Poke)(CPU|X|W|D|S)RegList is preferred.
  void PeekCPURegList(CPURegList registers, int64_t offset) {
    LoadCPURegList(registers, MemOperand(StackPointer(), offset));
  }
  void PokeCPURegList(CPURegList registers, int64_t offset) {
    StoreCPURegList(registers, MemOperand(StackPointer(), offset));
  }

  void PeekSizeRegList(RegList registers, int64_t offset, unsigned reg_size,
      CPURegister::RegisterType type = CPURegister::kRegister) {
    PeekCPURegList(CPURegList(type, reg_size, registers), offset);
  }
  void PokeSizeRegList(RegList registers, int64_t offset, unsigned reg_size,
      CPURegister::RegisterType type = CPURegister::kRegister) {
    PokeCPURegList(CPURegList(type, reg_size, registers), offset);
  }
  void PeekXRegList(RegList regs, int64_t offset) {
    PeekSizeRegList(regs, offset, kXRegSize);
  }
  void PokeXRegList(RegList regs, int64_t offset) {
    PokeSizeRegList(regs, offset, kXRegSize);
  }
  void PeekWRegList(RegList regs, int64_t offset) {
    PeekSizeRegList(regs, offset, kWRegSize);
  }
  void PokeWRegList(RegList regs, int64_t offset) {
    PokeSizeRegList(regs, offset, kWRegSize);
  }
  void PeekDRegList(RegList regs, int64_t offset) {
    PeekSizeRegList(regs, offset, kDRegSize, CPURegister::kVRegister);
  }
  void PokeDRegList(RegList regs, int64_t offset) {
    PokeSizeRegList(regs, offset, kDRegSize, CPURegister::kVRegister);
  }
  void PeekSRegList(RegList regs, int64_t offset) {
    PeekSizeRegList(regs, offset, kSRegSize, CPURegister::kVRegister);
  }
  void PokeSRegList(RegList regs, int64_t offset) {
    PokeSizeRegList(regs, offset, kSRegSize, CPURegister::kVRegister);
  }


  // Claim or drop stack space without actually accessing memory.
  //
  // If the current stack pointer (as set by SetStackPointer) is sp, then it
  // must be aligned to 16 bytes and the size claimed or dropped must be a
  // multiple of 16 bytes.
  void Claim(const Operand& size);
  void Drop(const Operand& size);

  // Preserve the callee-saved registers (as defined by AAPCS64).
  //
  // Higher-numbered registers are pushed before lower-numbered registers, and
  // thus get higher addresses.
  // Floating-point registers are pushed before general-purpose registers, and
  // thus get higher addresses.
  //
  // This method must not be called unless StackPointer() is sp, and it is
  // aligned to 16 bytes.
  void PushCalleeSavedRegisters();

  // Restore the callee-saved registers (as defined by AAPCS64).
  //
  // Higher-numbered registers are popped after lower-numbered registers, and
  // thus come from higher addresses.
  // Floating-point registers are popped after general-purpose registers, and
  // thus come from higher addresses.
  //
  // This method must not be called unless StackPointer() is sp, and it is
  // aligned to 16 bytes.
  void PopCalleeSavedRegisters();

  void LoadCPURegList(CPURegList registers, const MemOperand& src);
  void StoreCPURegList(CPURegList registers, const MemOperand& dst);

  // Remaining instructions are simple pass-through calls to the assembler.
  void Adr(const Register& rd, Label* label) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    adr(rd, label);
  }
  void Adrp(const Register& rd, Label* label) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    adrp(rd, label);
  }
  void Asr(const Register& rd, const Register& rn, unsigned shift) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    asr(rd, rn, shift);
  }
  void Asr(const Register& rd, const Register& rn, const Register& rm) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    SingleEmissionCheckScope guard(this);
    asrv(rd, rn, rm);
  }

  // Branch type inversion relies on these relations.
  VIXL_STATIC_ASSERT((reg_zero      == (reg_not_zero ^ 1)) &&
                     (reg_bit_clear == (reg_bit_set ^ 1)) &&
                     (always        == (never ^ 1)));

  BranchType InvertBranchType(BranchType type) {
    if (kBranchTypeFirstCondition <= type && type <= kBranchTypeLastCondition) {
      return static_cast<BranchType>(
          InvertCondition(static_cast<Condition>(type)));
    } else {
      return static_cast<BranchType>(type ^ 1);
    }
  }

  void B(Label* label, BranchType type, Register reg = NoReg, int bit = -1);

  void B(Label* label);
  void B(Label* label, Condition cond);
  void B(Condition cond, Label* label) {
    B(label, cond);
  }
  void Bfm(const Register& rd,
           const Register& rn,
           unsigned immr,
           unsigned imms) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    bfm(rd, rn, immr, imms);
  }
  void Bfi(const Register& rd,
           const Register& rn,
           unsigned lsb,
           unsigned width) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    bfi(rd, rn, lsb, width);
  }
  void Bfxil(const Register& rd,
             const Register& rn,
             unsigned lsb,
             unsigned width) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    bfxil(rd, rn, lsb, width);
  }
  void Bind(Label* label);
  // Bind a label to a specified offset from the start of the buffer.
  void BindToOffset(Label* label, ptrdiff_t offset);
  void Bl(Label* label) {
    SingleEmissionCheckScope guard(this);
    bl(label);
  }
  void Blr(const Register& xn) {
    VIXL_ASSERT(!xn.IsZero());
    SingleEmissionCheckScope guard(this);
    blr(xn);
  }
  void Br(const Register& xn) {
    VIXL_ASSERT(!xn.IsZero());
    SingleEmissionCheckScope guard(this);
    br(xn);
  }
  void Brk(int code = 0) {
    SingleEmissionCheckScope guard(this);
    brk(code);
  }
  void Cbnz(const Register& rt, Label* label);
  void Cbz(const Register& rt, Label* label);
  void Cinc(const Register& rd, const Register& rn, Condition cond) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    cinc(rd, rn, cond);
  }
  void Cinv(const Register& rd, const Register& rn, Condition cond) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    cinv(rd, rn, cond);
  }
  void Clrex() {
    SingleEmissionCheckScope guard(this);
    clrex();
  }
  void Cls(const Register& rd, const Register& rn) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    cls(rd, rn);
  }
  void Clz(const Register& rd, const Register& rn) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    clz(rd, rn);
  }
  void Cneg(const Register& rd, const Register& rn, Condition cond) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    cneg(rd, rn, cond);
  }
  void Cset(const Register& rd, Condition cond) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    cset(rd, cond);
  }
  void Csetm(const Register& rd, Condition cond) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    csetm(rd, cond);
  }
  void Csinc(const Register& rd,
             const Register& rn,
             const Register& rm,
             Condition cond) {
    VIXL_ASSERT(!rd.IsZero());
    // The VIXL source code contains these assertions, but the AArch64 ISR
    // explicitly permits the use of zero registers. CSET itself is defined
    // in terms of CSINC with WZR/XZR.
    //
    // VIXL_ASSERT(!rn.IsZero());
    // VIXL_ASSERT(!rm.IsZero());
    VIXL_ASSERT((cond != al) && (cond != nv));
    SingleEmissionCheckScope guard(this);
    csinc(rd, rn, rm, cond);
  }
  void Csinv(const Register& rd,
             const Register& rn,
             const Register& rm,
             Condition cond) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    VIXL_ASSERT((cond != al) && (cond != nv));
    SingleEmissionCheckScope guard(this);
    csinv(rd, rn, rm, cond);
  }
  void Csneg(const Register& rd,
             const Register& rn,
             const Register& rm,
             Condition cond) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    VIXL_ASSERT((cond != al) && (cond != nv));
    SingleEmissionCheckScope guard(this);
    csneg(rd, rn, rm, cond);
  }
  void Dmb(BarrierDomain domain, BarrierType type) {
    SingleEmissionCheckScope guard(this);
    dmb(domain, type);
  }
  void Dsb(BarrierDomain domain, BarrierType type) {
    SingleEmissionCheckScope guard(this);
    dsb(domain, type);
  }
  void Extr(const Register& rd,
            const Register& rn,
            const Register& rm,
            unsigned lsb) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    SingleEmissionCheckScope guard(this);
    extr(rd, rn, rm, lsb);
  }
  void Fadd(const VRegister& vd, const VRegister& vn, const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    fadd(vd, vn, vm);
  }
  void Fccmp(const VRegister& vn,
             const VRegister& vm,
             StatusFlags nzcv,
             Condition cond,
             FPTrapFlags trap = DisableTrap) {
    VIXL_ASSERT((cond != al) && (cond != nv));
    SingleEmissionCheckScope guard(this);
    FPCCompareMacro(vn, vm, nzcv, cond, trap);
  }
  void Fccmpe(const VRegister& vn,
              const VRegister& vm,
              StatusFlags nzcv,
              Condition cond) {
    Fccmp(vn, vm, nzcv, cond, EnableTrap);
  }
  void Fcmp(const VRegister& vn, const VRegister& vm,
            FPTrapFlags trap = DisableTrap) {
    SingleEmissionCheckScope guard(this);
    FPCompareMacro(vn, vm, trap);
  }
  void Fcmp(const VRegister& vn, double value,
            FPTrapFlags trap = DisableTrap);
  void Fcmpe(const VRegister& vn, double value);
  void Fcmpe(const VRegister& vn, const VRegister& vm) {
    Fcmp(vn, vm, EnableTrap);
  }
  void Fcsel(const VRegister& vd,
             const VRegister& vn,
             const VRegister& vm,
             Condition cond) {
    VIXL_ASSERT((cond != al) && (cond != nv));
    SingleEmissionCheckScope guard(this);
    fcsel(vd, vn, vm, cond);
  }
  void Fcvt(const VRegister& vd, const VRegister& vn) {
    SingleEmissionCheckScope guard(this);
    fcvt(vd, vn);
  }
  void Fcvtl(const VRegister& vd, const VRegister& vn) {
    SingleEmissionCheckScope guard(this);
    fcvtl(vd, vn);
  }
  void Fcvtl2(const VRegister& vd, const VRegister& vn) {
    SingleEmissionCheckScope guard(this);
    fcvtl2(vd, vn);
  }
  void Fcvtn(const VRegister& vd, const VRegister& vn) {
    SingleEmissionCheckScope guard(this);
    fcvtn(vd, vn);
  }
  void Fcvtn2(const VRegister& vd, const VRegister& vn) {
    SingleEmissionCheckScope guard(this);
    fcvtn2(vd, vn);
  }
  void Fcvtxn(const VRegister& vd, const VRegister& vn) {
    SingleEmissionCheckScope guard(this);
    fcvtxn(vd, vn);
  }
  void Fcvtxn2(const VRegister& vd, const VRegister& vn) {
    SingleEmissionCheckScope guard(this);
    fcvtxn2(vd, vn);
  }
  void Fcvtas(const Register& rd, const VRegister& vn) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    fcvtas(rd, vn);
  }
  void Fcvtau(const Register& rd, const VRegister& vn) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    fcvtau(rd, vn);
  }
  void Fcvtms(const Register& rd, const VRegister& vn) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    fcvtms(rd, vn);
  }
  void Fcvtmu(const Register& rd, const VRegister& vn) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    fcvtmu(rd, vn);
  }
  void Fcvtns(const Register& rd, const VRegister& vn) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    fcvtns(rd, vn);
  }
  void Fcvtnu(const Register& rd, const VRegister& vn) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    fcvtnu(rd, vn);
  }
  void Fcvtps(const Register& rd, const VRegister& vn) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    fcvtps(rd, vn);
  }
  void Fcvtpu(const Register& rd, const VRegister& vn) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    fcvtpu(rd, vn);
  }
  void Fcvtzs(const Register& rd, const VRegister& vn, int fbits = 0) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    fcvtzs(rd, vn, fbits);
  }
  void Fjcvtzs(const Register& rd, const VRegister& vn) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    fjcvtzs(rd, vn);
  }
  void Fcvtzu(const Register& rd, const VRegister& vn, int fbits = 0) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    fcvtzu(rd, vn, fbits);
  }
  void Fdiv(const VRegister& vd, const VRegister& vn, const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    fdiv(vd, vn, vm);
  }
  void Fmax(const VRegister& vd, const VRegister& vn, const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    fmax(vd, vn, vm);
  }
  void Fmaxnm(const VRegister& vd,
              const VRegister& vn,
              const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    fmaxnm(vd, vn, vm);
  }
  void Fmin(const VRegister& vd, const VRegister& vn, const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    fmin(vd, vn, vm);
  }
  void Fminnm(const VRegister& vd,
              const VRegister& vn,
              const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    fminnm(vd, vn, vm);
  }
  void Fmov(VRegister vd, VRegister vn) {
    SingleEmissionCheckScope guard(this);
    // Only emit an instruction if vd and vn are different, and they are both D
    // registers. fmov(s0, s0) is not a no-op because it clears the top word of
    // d0. Technically, fmov(d0, d0) is not a no-op either because it clears
    // the top of q0, but VRegister does not currently support Q registers.
    if (!vd.Is(vn) || !vd.Is64Bits()) {
      fmov(vd, vn);
    }
  }
  void Fmov(VRegister vd, Register rn) {
    SingleEmissionCheckScope guard(this);
    fmov(vd, rn);
  }
  void Fmov(const VRegister& vd, int index, const Register& rn) {
    SingleEmissionCheckScope guard(this);
    fmov(vd, index, rn);
  }
  void Fmov(const Register& rd, const VRegister& vn, int index) {
    SingleEmissionCheckScope guard(this);
    fmov(rd, vn, index);
  }

  // Provide explicit double and float interfaces for FP immediate moves, rather
  // than relying on implicit C++ casts. This allows signalling NaNs to be
  // preserved when the immediate matches the format of vd. Most systems convert
  // signalling NaNs to quiet NaNs when converting between float and double.
  void Fmov(VRegister vd, double imm);
  void Fmov(VRegister vd, float imm);
  // Provide a template to allow other types to be converted automatically.
  template<typename T>
  void Fmov(VRegister vd, T imm) {
    Fmov(vd, static_cast<double>(imm));
  }
  void Fmov(Register rd, VRegister vn) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    fmov(rd, vn);
  }
  void Fmul(const VRegister& vd, const VRegister& vn, const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    fmul(vd, vn, vm);
  }
  void Fnmul(const VRegister& vd, const VRegister& vn,
             const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    fnmul(vd, vn, vm);
  }
  void Fmadd(const VRegister& vd,
             const VRegister& vn,
             const VRegister& vm,
             const VRegister& va) {
    SingleEmissionCheckScope guard(this);
    fmadd(vd, vn, vm, va);
  }
  void Fmsub(const VRegister& vd,
             const VRegister& vn,
             const VRegister& vm,
             const VRegister& va) {
    SingleEmissionCheckScope guard(this);
    fmsub(vd, vn, vm, va);
  }
  void Fnmadd(const VRegister& vd,
              const VRegister& vn,
              const VRegister& vm,
              const VRegister& va) {
    SingleEmissionCheckScope guard(this);
    fnmadd(vd, vn, vm, va);
  }
  void Fnmsub(const VRegister& vd,
              const VRegister& vn,
              const VRegister& vm,
              const VRegister& va) {
    SingleEmissionCheckScope guard(this);
    fnmsub(vd, vn, vm, va);
  }
  void Fsub(const VRegister& vd, const VRegister& vn, const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    fsub(vd, vn, vm);
  }
  void Hint(SystemHint code) {
    SingleEmissionCheckScope guard(this);
    hint(code);
  }
  void Hlt(int code) {
    SingleEmissionCheckScope guard(this);
    hlt(code);
  }
  void Isb() {
    SingleEmissionCheckScope guard(this);
    isb();
  }
  void Ldar(const Register& rt, const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ldar(rt, src);
  }
  void Ldarb(const Register& rt, const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ldarb(rt, src);
  }
  void Ldarh(const Register& rt, const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ldarh(rt, src);
  }
  void Ldaxp(const Register& rt, const Register& rt2, const MemOperand& src) {
    VIXL_ASSERT(!rt.Aliases(rt2));
    SingleEmissionCheckScope guard(this);
    ldaxp(rt, rt2, src);
  }
  void Ldaxr(const Register& rt, const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ldaxr(rt, src);
  }
  void Ldaxrb(const Register& rt, const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ldaxrb(rt, src);
  }
  void Ldaxrh(const Register& rt, const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ldaxrh(rt, src);
  }

// clang-format off
#define COMPARE_AND_SWAP_SINGLE_MACRO_LIST(V) \
  V(cas,    Cas)                              \
  V(casa,   Casa)                             \
  V(casl,   Casl)                             \
  V(casal,  Casal)                            \
  V(casb,   Casb)                             \
  V(casab,  Casab)                            \
  V(caslb,  Caslb)                            \
  V(casalb, Casalb)                           \
  V(cash,   Cash)                             \
  V(casah,  Casah)                            \
  V(caslh,  Caslh)                            \
  V(casalh, Casalh)
  // clang-format on

#define DEFINE_MACRO_ASM_FUNC(ASM, MASM)                                     \
  void MASM(const Register& rs, const Register& rt, const MemOperand& src) { \
    SingleEmissionCheckScope guard(this);                                    \
    ASM(rs, rt, src);                                                        \
  }
  COMPARE_AND_SWAP_SINGLE_MACRO_LIST(DEFINE_MACRO_ASM_FUNC)
#undef DEFINE_MACRO_ASM_FUNC

// clang-format off
#define COMPARE_AND_SWAP_PAIR_MACRO_LIST(V) \
  V(casp,   Casp)                           \
  V(caspa,  Caspa)                          \
  V(caspl,  Caspl)                          \
  V(caspal, Caspal)
  // clang-format on

#define DEFINE_MACRO_ASM_FUNC(ASM, MASM)                                 \
  void MASM(const Register& rs, const Register& rs2, const Register& rt, \
            const Register& rt2, const MemOperand& src) {                \
    SingleEmissionCheckScope guard(this);                                \
    ASM(rs, rs2, rt, rt2, src);                                          \
  }
  COMPARE_AND_SWAP_PAIR_MACRO_LIST(DEFINE_MACRO_ASM_FUNC)
#undef DEFINE_MACRO_ASM_FUNC

// These macros generate all the variations of the atomic memory operations,
// e.g. ldadd, ldadda, ldaddb, staddl, etc.

// clang-format off
#define ATOMIC_MEMORY_SIMPLE_MACRO_LIST(V, DEF, MASM_PRE, ASM_PRE) \
  V(DEF, MASM_PRE##add,  ASM_PRE##add)                             \
  V(DEF, MASM_PRE##clr,  ASM_PRE##clr)                             \
  V(DEF, MASM_PRE##eor,  ASM_PRE##eor)                             \
  V(DEF, MASM_PRE##set,  ASM_PRE##set)                             \
  V(DEF, MASM_PRE##smax, ASM_PRE##smax)                            \
  V(DEF, MASM_PRE##smin, ASM_PRE##smin)                            \
  V(DEF, MASM_PRE##umax, ASM_PRE##umax)                            \
  V(DEF, MASM_PRE##umin, ASM_PRE##umin)

#define ATOMIC_MEMORY_STORE_MACRO_MODES(V, MASM, ASM) \
  V(MASM,     ASM)                                    \
  V(MASM##l,  ASM##l)                                 \
  V(MASM##b,  ASM##b)                                 \
  V(MASM##lb, ASM##lb)                                \
  V(MASM##h,  ASM##h)                                 \
  V(MASM##lh, ASM##lh)

#define ATOMIC_MEMORY_LOAD_MACRO_MODES(V, MASM, ASM) \
  ATOMIC_MEMORY_STORE_MACRO_MODES(V, MASM, ASM)      \
  V(MASM##a,   ASM##a)                               \
  V(MASM##al,  ASM##al)                              \
  V(MASM##ab,  ASM##ab)                              \
  V(MASM##alb, ASM##alb)                             \
  V(MASM##ah,  ASM##ah)                              \
  V(MASM##alh, ASM##alh)
  // clang-format on

#define DEFINE_MACRO_LOAD_ASM_FUNC(MASM, ASM)                                \
  void MASM(const Register& rs, const Register& rt, const MemOperand& src) { \
    SingleEmissionCheckScope guard(this);                                    \
    ASM(rs, rt, src);                                                        \
  }
#define DEFINE_MACRO_STORE_ASM_FUNC(MASM, ASM)           \
  void MASM(const Register& rs, const MemOperand& src) { \
    SingleEmissionCheckScope guard(this);                \
    ASM(rs, src);                                        \
  }

  ATOMIC_MEMORY_SIMPLE_MACRO_LIST(ATOMIC_MEMORY_LOAD_MACRO_MODES,
                                  DEFINE_MACRO_LOAD_ASM_FUNC,
                                  Ld,
                                  ld)
  ATOMIC_MEMORY_SIMPLE_MACRO_LIST(ATOMIC_MEMORY_STORE_MACRO_MODES,
                                  DEFINE_MACRO_STORE_ASM_FUNC,
                                  St,
                                  st)

#define DEFINE_MACRO_SWP_ASM_FUNC(MASM, ASM)                                 \
  void MASM(const Register& rs, const Register& rt, const MemOperand& src) { \
    SingleEmissionCheckScope guard(this);                                    \
    ASM(rs, rt, src);                                                        \
  }

  ATOMIC_MEMORY_LOAD_MACRO_MODES(DEFINE_MACRO_SWP_ASM_FUNC, Swp, swp)

#undef DEFINE_MACRO_LOAD_ASM_FUNC
#undef DEFINE_MACRO_STORE_ASM_FUNC
#undef DEFINE_MACRO_SWP_ASM_FUNC

  void Ldnp(const CPURegister& rt,
            const CPURegister& rt2,
            const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ldnp(rt, rt2, src);
  }
  // Provide both double and float interfaces for FP immediate loads, rather
  // than relying on implicit C++ casts. This allows signalling NaNs to be
  // preserved when the immediate matches the format of fd. Most systems convert
  // signalling NaNs to quiet NaNs when converting between float and double.
  void Ldr(const VRegister& vt, double imm) {
    SingleEmissionCheckScope guard(this);
    if (vt.Is64Bits()) {
      ldr(vt, imm);
    } else {
      ldr(vt, static_cast<float>(imm));
    }
  }
  void Ldr(const VRegister& vt, float imm) {
    SingleEmissionCheckScope guard(this);
    if (vt.Is32Bits()) {
      ldr(vt, imm);
    } else {
      ldr(vt, static_cast<double>(imm));
    }
  }
  /*
  void Ldr(const VRegister& vt, uint64_t high64, uint64_t low64) {
    VIXL_ASSERT(vt.IsQ());
    SingleEmissionCheckScope guard(this);
    ldr(vt, new Literal<uint64_t>(high64, low64,
                                  &literal_pool_,
                                  RawLiteral::kDeletedOnPlacementByPool));
  }
  */
  void Ldr(const Register& rt, uint64_t imm) {
    VIXL_ASSERT(!rt.IsZero());
    SingleEmissionCheckScope guard(this);
    ldr(rt, imm);
  }
  void Ldrsw(const Register& rt, uint32_t imm) {
    VIXL_ASSERT(!rt.IsZero());
    SingleEmissionCheckScope guard(this);
    ldrsw(rt, imm);
  }
  void Ldxp(const Register& rt, const Register& rt2, const MemOperand& src) {
    VIXL_ASSERT(!rt.Aliases(rt2));
    SingleEmissionCheckScope guard(this);
    ldxp(rt, rt2, src);
  }
  void Ldxr(const Register& rt, const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ldxr(rt, src);
  }
  void Ldxrb(const Register& rt, const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ldxrb(rt, src);
  }
  void Ldxrh(const Register& rt, const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ldxrh(rt, src);
  }
  void Lsl(const Register& rd, const Register& rn, unsigned shift) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    lsl(rd, rn, shift);
  }
  void Lsl(const Register& rd, const Register& rn, const Register& rm) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    SingleEmissionCheckScope guard(this);
    lslv(rd, rn, rm);
  }
  void Lsr(const Register& rd, const Register& rn, unsigned shift) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    lsr(rd, rn, shift);
  }
  void Lsr(const Register& rd, const Register& rn, const Register& rm) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    SingleEmissionCheckScope guard(this);
    lsrv(rd, rn, rm);
  }
  void Madd(const Register& rd,
            const Register& rn,
            const Register& rm,
            const Register& ra) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    VIXL_ASSERT(!ra.IsZero());
    SingleEmissionCheckScope guard(this);
    madd(rd, rn, rm, ra);
  }
  void Mneg(const Register& rd, const Register& rn, const Register& rm) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    SingleEmissionCheckScope guard(this);
    mneg(rd, rn, rm);
  }
  void Mov(const Register& rd, const Register& rn) {
    SingleEmissionCheckScope guard(this);
    mov(rd, rn);
  }
  void Movk(const Register& rd, uint64_t imm, int shift = -1) {
    VIXL_ASSERT(!rd.IsZero());
    SingleEmissionCheckScope guard(this);
    movk(rd, imm, shift);
  }
  void Mrs(const Register& rt, SystemRegister sysreg) {
    VIXL_ASSERT(!rt.IsZero());
    SingleEmissionCheckScope guard(this);
    mrs(rt, sysreg);
  }
  void Msr(SystemRegister sysreg, const Register& rt) {
    VIXL_ASSERT(!rt.IsZero());
    SingleEmissionCheckScope guard(this);
    msr(sysreg, rt);
  }
  void Sys(int op1, int crn, int crm, int op2, const Register& rt = xzr) {
    SingleEmissionCheckScope guard(this);
    sys(op1, crn, crm, op2, rt);
  }
  void Dc(DataCacheOp op, const Register& rt) {
    SingleEmissionCheckScope guard(this);
    dc(op, rt);
  }
  void Ic(InstructionCacheOp op, const Register& rt) {
    SingleEmissionCheckScope guard(this);
    ic(op, rt);
  }
  void Msub(const Register& rd,
            const Register& rn,
            const Register& rm,
            const Register& ra) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    VIXL_ASSERT(!ra.IsZero());
    SingleEmissionCheckScope guard(this);
    msub(rd, rn, rm, ra);
  }
  void Mul(const Register& rd, const Register& rn, const Register& rm) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    SingleEmissionCheckScope guard(this);
    mul(rd, rn, rm);
  }
  void Nop() {
    SingleEmissionCheckScope guard(this);
    nop();
  }
  void Csdb() {
    SingleEmissionCheckScope guard(this);
    csdb();
  }
  void Rbit(const Register& rd, const Register& rn) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    rbit(rd, rn);
  }
  void Ret(const Register& xn = lr) {
    VIXL_ASSERT(!xn.IsZero());
    SingleEmissionCheckScope guard(this);
    ret(xn);
  }
  void Rev(const Register& rd, const Register& rn) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    rev(rd, rn);
  }
  void Rev16(const Register& rd, const Register& rn) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    rev16(rd, rn);
  }
  void Rev32(const Register& rd, const Register& rn) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    rev32(rd, rn);
  }
  void Ror(const Register& rd, const Register& rs, unsigned shift) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rs.IsZero());
    SingleEmissionCheckScope guard(this);
    ror(rd, rs, shift);
  }
  void Ror(const Register& rd, const Register& rn, const Register& rm) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    SingleEmissionCheckScope guard(this);
    rorv(rd, rn, rm);
  }
  void Sbfiz(const Register& rd,
             const Register& rn,
             unsigned lsb,
             unsigned width) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    sbfiz(rd, rn, lsb, width);
  }
  void Sbfm(const Register& rd,
            const Register& rn,
            unsigned immr,
            unsigned imms) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    sbfm(rd, rn, immr, imms);
  }
  void Sbfx(const Register& rd,
            const Register& rn,
            unsigned lsb,
            unsigned width) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    sbfx(rd, rn, lsb, width);
  }
  void Scvtf(const VRegister& vd, const Register& rn, int fbits = 0) {
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    scvtf(vd, rn, fbits);
  }
  void Sdiv(const Register& rd, const Register& rn, const Register& rm) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    SingleEmissionCheckScope guard(this);
    sdiv(rd, rn, rm);
  }
  void Smaddl(const Register& rd,
              const Register& rn,
              const Register& rm,
              const Register& ra) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    VIXL_ASSERT(!ra.IsZero());
    SingleEmissionCheckScope guard(this);
    smaddl(rd, rn, rm, ra);
  }
  void Smsubl(const Register& rd,
              const Register& rn,
              const Register& rm,
              const Register& ra) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    VIXL_ASSERT(!ra.IsZero());
    SingleEmissionCheckScope guard(this);
    smsubl(rd, rn, rm, ra);
  }
  void Smull(const Register& rd, const Register& rn, const Register& rm) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    SingleEmissionCheckScope guard(this);
    smull(rd, rn, rm);
  }
  void Smulh(const Register& xd, const Register& xn, const Register& xm) {
    VIXL_ASSERT(!xd.IsZero());
    VIXL_ASSERT(!xn.IsZero());
    VIXL_ASSERT(!xm.IsZero());
    SingleEmissionCheckScope guard(this);
    smulh(xd, xn, xm);
  }
  void Stlr(const Register& rt, const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    stlr(rt, dst);
  }
  void Stlrb(const Register& rt, const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    stlrb(rt, dst);
  }
  void Stlrh(const Register& rt, const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    stlrh(rt, dst);
  }
  void Stlxp(const Register& rs,
             const Register& rt,
             const Register& rt2,
             const MemOperand& dst) {
    VIXL_ASSERT(!rs.Aliases(dst.base()));
    VIXL_ASSERT(!rs.Aliases(rt));
    VIXL_ASSERT(!rs.Aliases(rt2));
    SingleEmissionCheckScope guard(this);
    stlxp(rs, rt, rt2, dst);
  }
  void Stlxr(const Register& rs, const Register& rt, const MemOperand& dst) {
    VIXL_ASSERT(!rs.Aliases(dst.base()));
    VIXL_ASSERT(!rs.Aliases(rt));
    SingleEmissionCheckScope guard(this);
    stlxr(rs, rt, dst);
  }
  void Stlxrb(const Register& rs, const Register& rt, const MemOperand& dst) {
    VIXL_ASSERT(!rs.Aliases(dst.base()));
    VIXL_ASSERT(!rs.Aliases(rt));
    SingleEmissionCheckScope guard(this);
    stlxrb(rs, rt, dst);
  }
  void Stlxrh(const Register& rs, const Register& rt, const MemOperand& dst) {
    VIXL_ASSERT(!rs.Aliases(dst.base()));
    VIXL_ASSERT(!rs.Aliases(rt));
    SingleEmissionCheckScope guard(this);
    stlxrh(rs, rt, dst);
  }
  void Stnp(const CPURegister& rt,
            const CPURegister& rt2,
            const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    stnp(rt, rt2, dst);
  }
  void Stxp(const Register& rs,
            const Register& rt,
            const Register& rt2,
            const MemOperand& dst) {
    VIXL_ASSERT(!rs.Aliases(dst.base()));
    VIXL_ASSERT(!rs.Aliases(rt));
    VIXL_ASSERT(!rs.Aliases(rt2));
    SingleEmissionCheckScope guard(this);
    stxp(rs, rt, rt2, dst);
  }
  void Stxr(const Register& rs, const Register& rt, const MemOperand& dst) {
    VIXL_ASSERT(!rs.Aliases(dst.base()));
    VIXL_ASSERT(!rs.Aliases(rt));
    SingleEmissionCheckScope guard(this);
    stxr(rs, rt, dst);
  }
  void Stxrb(const Register& rs, const Register& rt, const MemOperand& dst) {
    VIXL_ASSERT(!rs.Aliases(dst.base()));
    VIXL_ASSERT(!rs.Aliases(rt));
    SingleEmissionCheckScope guard(this);
    stxrb(rs, rt, dst);
  }
  void Stxrh(const Register& rs, const Register& rt, const MemOperand& dst) {
    VIXL_ASSERT(!rs.Aliases(dst.base()));
    VIXL_ASSERT(!rs.Aliases(rt));
    SingleEmissionCheckScope guard(this);
    stxrh(rs, rt, dst);
  }
  void Svc(int code) {
    SingleEmissionCheckScope guard(this);
    svc(code);
  }
  void Sxtb(const Register& rd, const Register& rn) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    sxtb(rd, rn);
  }
  void Sxth(const Register& rd, const Register& rn) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    sxth(rd, rn);
  }
  void Sxtw(const Register& rd, const Register& rn) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    sxtw(rd, rn);
  }
  void Tbl(const VRegister& vd,
           const VRegister& vn,
           const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    tbl(vd, vn, vm);
  }
  void Tbl(const VRegister& vd,
           const VRegister& vn,
           const VRegister& vn2,
           const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    tbl(vd, vn, vn2, vm);
  }
  void Tbl(const VRegister& vd,
           const VRegister& vn,
           const VRegister& vn2,
           const VRegister& vn3,
           const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    tbl(vd, vn, vn2, vn3, vm);
  }
  void Tbl(const VRegister& vd,
           const VRegister& vn,
           const VRegister& vn2,
           const VRegister& vn3,
           const VRegister& vn4,
           const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    tbl(vd, vn, vn2, vn3, vn4, vm);
  }
  void Tbx(const VRegister& vd,
           const VRegister& vn,
           const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    tbx(vd, vn, vm);
  }
  void Tbx(const VRegister& vd,
           const VRegister& vn,
           const VRegister& vn2,
           const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    tbx(vd, vn, vn2, vm);
  }
  void Tbx(const VRegister& vd,
           const VRegister& vn,
           const VRegister& vn2,
           const VRegister& vn3,
           const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    tbx(vd, vn, vn2, vn3, vm);
  }
  void Tbx(const VRegister& vd,
           const VRegister& vn,
           const VRegister& vn2,
           const VRegister& vn3,
           const VRegister& vn4,
           const VRegister& vm) {
    SingleEmissionCheckScope guard(this);
    tbx(vd, vn, vn2, vn3, vn4, vm);
  }
  void Tbnz(const Register& rt, unsigned bit_pos, Label* label);
  void Tbz(const Register& rt, unsigned bit_pos, Label* label);
  void Ubfiz(const Register& rd,
             const Register& rn,
             unsigned lsb,
             unsigned width) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    ubfiz(rd, rn, lsb, width);
  }
  void Ubfm(const Register& rd,
            const Register& rn,
            unsigned immr,
            unsigned imms) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    ubfm(rd, rn, immr, imms);
  }
  void Ubfx(const Register& rd,
            const Register& rn,
            unsigned lsb,
            unsigned width) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    ubfx(rd, rn, lsb, width);
  }
  void Ucvtf(const VRegister& vd, const Register& rn, int fbits = 0) {
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    ucvtf(vd, rn, fbits);
  }
  void Udiv(const Register& rd, const Register& rn, const Register& rm) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    SingleEmissionCheckScope guard(this);
    udiv(rd, rn, rm);
  }
  void Umaddl(const Register& rd,
              const Register& rn,
              const Register& rm,
              const Register& ra) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    VIXL_ASSERT(!ra.IsZero());
    SingleEmissionCheckScope guard(this);
    umaddl(rd, rn, rm, ra);
  }
  void Umull(const Register& rd,
             const Register& rn,
             const Register& rm) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    SingleEmissionCheckScope guard(this);
    umull(rd, rn, rm);
  }
  void Umulh(const Register& xd, const Register& xn, const Register& xm) {
    VIXL_ASSERT(!xd.IsZero());
    VIXL_ASSERT(!xn.IsZero());
    VIXL_ASSERT(!xm.IsZero());
    SingleEmissionCheckScope guard(this);
    umulh(xd, xn, xm);
  }
  void Umsubl(const Register& rd,
              const Register& rn,
              const Register& rm,
              const Register& ra) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    VIXL_ASSERT(!rm.IsZero());
    VIXL_ASSERT(!ra.IsZero());
    SingleEmissionCheckScope guard(this);
    umsubl(rd, rn, rm, ra);
  }

  void Unreachable() {
    SingleEmissionCheckScope guard(this);
    Emit(UNDEFINED_INST_PATTERN);
  }

  void Uxtb(const Register& rd, const Register& rn) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    uxtb(rd, rn);
  }
  void Uxth(const Register& rd, const Register& rn) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    uxth(rd, rn);
  }
  void Uxtw(const Register& rd, const Register& rn) {
    VIXL_ASSERT(!rd.IsZero());
    VIXL_ASSERT(!rn.IsZero());
    SingleEmissionCheckScope guard(this);
    uxtw(rd, rn);
  }

  // NEON 3 vector register instructions.
  #define NEON_3VREG_MACRO_LIST(V) \
    V(add, Add)                    \
    V(addhn, Addhn)                \
    V(addhn2, Addhn2)              \
    V(addp, Addp)                  \
    V(and_, And)                   \
    V(bic, Bic)                    \
    V(bif, Bif)                    \
    V(bit, Bit)                    \
    V(bsl, Bsl)                    \
    V(cmeq, Cmeq)                  \
    V(cmge, Cmge)                  \
    V(cmgt, Cmgt)                  \
    V(cmhi, Cmhi)                  \
    V(cmhs, Cmhs)                  \
    V(cmtst, Cmtst)                \
    V(eor, Eor)                    \
    V(fabd, Fabd)                  \
    V(facge, Facge)                \
    V(facgt, Facgt)                \
    V(faddp, Faddp)                \
    V(fcmeq, Fcmeq)                \
    V(fcmge, Fcmge)                \
    V(fcmgt, Fcmgt)                \
    V(fmaxnmp, Fmaxnmp)            \
    V(fmaxp, Fmaxp)                \
    V(fminnmp, Fminnmp)            \
    V(fminp, Fminp)                \
    V(fmla, Fmla)                  \
    V(fmls, Fmls)                  \
    V(fmulx, Fmulx)                \
    V(frecps, Frecps)              \
    V(frsqrts, Frsqrts)            \
    V(mla, Mla)                    \
    V(mls, Mls)                    \
    V(mul, Mul)                    \
    V(orn, Orn)                    \
    V(orr, Orr)                    \
    V(pmul, Pmul)                  \
    V(pmull, Pmull)                \
    V(pmull2, Pmull2)              \
    V(raddhn, Raddhn)              \
    V(raddhn2, Raddhn2)            \
    V(rsubhn, Rsubhn)              \
    V(rsubhn2, Rsubhn2)            \
    V(saba, Saba)                  \
    V(sabal, Sabal)                \
    V(sabal2, Sabal2)              \
    V(sabd, Sabd)                  \
    V(sabdl, Sabdl)                \
    V(sabdl2, Sabdl2)              \
    V(saddl, Saddl)                \
    V(saddl2, Saddl2)              \
    V(saddw, Saddw)                \
    V(saddw2, Saddw2)              \
    V(shadd, Shadd)                \
    V(shsub, Shsub)                \
    V(smax, Smax)                  \
    V(smaxp, Smaxp)                \
    V(smin, Smin)                  \
    V(sminp, Sminp)                \
    V(smlal, Smlal)                \
    V(smlal2, Smlal2)              \
    V(smlsl, Smlsl)                \
    V(smlsl2, Smlsl2)              \
    V(smull, Smull)                \
    V(smull2, Smull2)              \
    V(sqadd, Sqadd)                \
    V(sqdmlal, Sqdmlal)            \
    V(sqdmlal2, Sqdmlal2)          \
    V(sqdmlsl, Sqdmlsl)            \
    V(sqdmlsl2, Sqdmlsl2)          \
    V(sqdmulh, Sqdmulh)            \
    V(sqdmull, Sqdmull)            \
    V(sqdmull2, Sqdmull2)          \
    V(sqrdmulh, Sqrdmulh)          \
    V(sqrshl, Sqrshl)              \
    V(sqshl, Sqshl)                \
    V(sqsub, Sqsub)                \
    V(srhadd, Srhadd)              \
    V(srshl, Srshl)                \
    V(sshl, Sshl)                  \
    V(ssubl, Ssubl)                \
    V(ssubl2, Ssubl2)              \
    V(ssubw, Ssubw)                \
    V(ssubw2, Ssubw2)              \
    V(sub, Sub)                    \
    V(subhn, Subhn)                \
    V(subhn2, Subhn2)              \
    V(trn1, Trn1)                  \
    V(trn2, Trn2)                  \
    V(uaba, Uaba)                  \
    V(uabal, Uabal)                \
    V(uabal2, Uabal2)              \
    V(uabd, Uabd)                  \
    V(uabdl, Uabdl)                \
    V(uabdl2, Uabdl2)              \
    V(uaddl, Uaddl)                \
    V(uaddl2, Uaddl2)              \
    V(uaddw, Uaddw)                \
    V(uaddw2, Uaddw2)              \
    V(uhadd, Uhadd)                \
    V(uhsub, Uhsub)                \
    V(umax, Umax)                  \
    V(umaxp, Umaxp)                \
    V(umin, Umin)                  \
    V(uminp, Uminp)                \
    V(umlal, Umlal)                \
    V(umlal2, Umlal2)              \
    V(umlsl, Umlsl)                \
    V(umlsl2, Umlsl2)              \
    V(umull, Umull)                \
    V(umull2, Umull2)              \
    V(uqadd, Uqadd)                \
    V(uqrshl, Uqrshl)              \
    V(uqshl, Uqshl)                \
    V(uqsub, Uqsub)                \
    V(urhadd, Urhadd)              \
    V(urshl, Urshl)                \
    V(ushl, Ushl)                  \
    V(usubl, Usubl)                \
    V(usubl2, Usubl2)              \
    V(usubw, Usubw)                \
    V(usubw2, Usubw2)              \
    V(uzp1, Uzp1)                  \
    V(uzp2, Uzp2)                  \
    V(zip1, Zip1)                  \
    V(zip2, Zip2)

  #define DEFINE_MACRO_ASM_FUNC(ASM, MASM)   \
  void MASM(const VRegister& vd,             \
            const VRegister& vn,             \
            const VRegister& vm) {           \
    SingleEmissionCheckScope guard(this);    \
    ASM(vd, vn, vm);                         \
  }
  NEON_3VREG_MACRO_LIST(DEFINE_MACRO_ASM_FUNC)
  #undef DEFINE_MACRO_ASM_FUNC

  // NEON 2 vector register instructions.
  #define NEON_2VREG_MACRO_LIST(V) \
    V(abs,     Abs)                \
    V(addp,    Addp)               \
    V(addv,    Addv)               \
    V(cls,     Cls)                \
    V(clz,     Clz)                \
    V(cnt,     Cnt)                \
    V(fabs,    Fabs)               \
    V(faddp,   Faddp)              \
    V(fcvtas,  Fcvtas)             \
    V(fcvtau,  Fcvtau)             \
    V(fcvtms,  Fcvtms)             \
    V(fcvtmu,  Fcvtmu)             \
    V(fcvtns,  Fcvtns)             \
    V(fcvtnu,  Fcvtnu)             \
    V(fcvtps,  Fcvtps)             \
    V(fcvtpu,  Fcvtpu)             \
    V(fmaxnmp, Fmaxnmp)            \
    V(fmaxnmv, Fmaxnmv)            \
    V(fmaxp,   Fmaxp)              \
    V(fmaxv,   Fmaxv)              \
    V(fminnmp, Fminnmp)            \
    V(fminnmv, Fminnmv)            \
    V(fminp,   Fminp)              \
    V(fminv,   Fminv)              \
    V(fneg,    Fneg)               \
    V(frecpe,  Frecpe)             \
    V(frecpx,  Frecpx)             \
    V(frinta,  Frinta)             \
    V(frinti,  Frinti)             \
    V(frintm,  Frintm)             \
    V(frintn,  Frintn)             \
    V(frintp,  Frintp)             \
    V(frintx,  Frintx)             \
    V(frintz,  Frintz)             \
    V(frsqrte, Frsqrte)            \
    V(fsqrt,   Fsqrt)              \
    V(mov,     Mov)                \
    V(mvn,     Mvn)                \
    V(neg,     Neg)                \
    V(not_,    Not)                \
    V(rbit,    Rbit)               \
    V(rev16,   Rev16)              \
    V(rev32,   Rev32)              \
    V(rev64,   Rev64)              \
    V(sadalp,  Sadalp)             \
    V(saddlp,  Saddlp)             \
    V(saddlv,  Saddlv)             \
    V(smaxv,   Smaxv)              \
    V(sminv,   Sminv)              \
    V(sqabs,   Sqabs)              \
    V(sqneg,   Sqneg)              \
    V(sqxtn,   Sqxtn)              \
    V(sqxtn2,  Sqxtn2)             \
    V(sqxtun,  Sqxtun)             \
    V(sqxtun2, Sqxtun2)            \
    V(suqadd,  Suqadd)             \
    V(sxtl,    Sxtl)               \
    V(sxtl2,   Sxtl2)              \
    V(uadalp,  Uadalp)             \
    V(uaddlp,  Uaddlp)             \
    V(uaddlv,  Uaddlv)             \
    V(umaxv,   Umaxv)              \
    V(uminv,   Uminv)              \
    V(uqxtn,   Uqxtn)              \
    V(uqxtn2,  Uqxtn2)             \
    V(urecpe,  Urecpe)             \
    V(ursqrte, Ursqrte)            \
    V(usqadd,  Usqadd)             \
    V(uxtl,    Uxtl)               \
    V(uxtl2,   Uxtl2)              \
    V(xtn,     Xtn)                \
    V(xtn2,    Xtn2)

  #define DEFINE_MACRO_ASM_FUNC(ASM, MASM)   \
  void MASM(const VRegister& vd,             \
            const VRegister& vn) {           \
    SingleEmissionCheckScope guard(this);    \
    ASM(vd, vn);                             \
  }
  NEON_2VREG_MACRO_LIST(DEFINE_MACRO_ASM_FUNC)
  #undef DEFINE_MACRO_ASM_FUNC

  // NEON 2 vector register with immediate instructions.
  #define NEON_2VREG_FPIMM_MACRO_LIST(V) \
    V(fcmeq, Fcmeq)                      \
    V(fcmge, Fcmge)                      \
    V(fcmgt, Fcmgt)                      \
    V(fcmle, Fcmle)                      \
    V(fcmlt, Fcmlt)

  #define DEFINE_MACRO_ASM_FUNC(ASM, MASM)   \
  void MASM(const VRegister& vd,             \
            const VRegister& vn,             \
            double imm) {                    \
    SingleEmissionCheckScope guard(this);    \
    ASM(vd, vn, imm);                        \
  }
  NEON_2VREG_FPIMM_MACRO_LIST(DEFINE_MACRO_ASM_FUNC)
  #undef DEFINE_MACRO_ASM_FUNC

  // NEON by element instructions.
  #define NEON_BYELEMENT_MACRO_LIST(V) \
    V(fmul, Fmul)                      \
    V(fmla, Fmla)                      \
    V(fmls, Fmls)                      \
    V(fmulx, Fmulx)                    \
    V(mul, Mul)                        \
    V(mla, Mla)                        \
    V(mls, Mls)                        \
    V(sqdmulh, Sqdmulh)                \
    V(sqrdmulh, Sqrdmulh)              \
    V(sqdmull,  Sqdmull)               \
    V(sqdmull2, Sqdmull2)              \
    V(sqdmlal,  Sqdmlal)               \
    V(sqdmlal2, Sqdmlal2)              \
    V(sqdmlsl,  Sqdmlsl)               \
    V(sqdmlsl2, Sqdmlsl2)              \
    V(smull,  Smull)                   \
    V(smull2, Smull2)                  \
    V(smlal,  Smlal)                   \
    V(smlal2, Smlal2)                  \
    V(smlsl,  Smlsl)                   \
    V(smlsl2, Smlsl2)                  \
    V(umull,  Umull)                   \
    V(umull2, Umull2)                  \
    V(umlal,  Umlal)                   \
    V(umlal2, Umlal2)                  \
    V(umlsl,  Umlsl)                   \
    V(umlsl2, Umlsl2)

  #define DEFINE_MACRO_ASM_FUNC(ASM, MASM)   \
  void MASM(const VRegister& vd,             \
            const VRegister& vn,             \
            const VRegister& vm,             \
            int vm_index                     \
            ) {                              \
    SingleEmissionCheckScope guard(this);    \
    ASM(vd, vn, vm, vm_index);               \
  }
  NEON_BYELEMENT_MACRO_LIST(DEFINE_MACRO_ASM_FUNC)
  #undef DEFINE_MACRO_ASM_FUNC

  #define NEON_2VREG_SHIFT_MACRO_LIST(V) \
    V(rshrn,     Rshrn)                  \
    V(rshrn2,    Rshrn2)                 \
    V(shl,       Shl)                    \
    V(shll,      Shll)                   \
    V(shll2,     Shll2)                  \
    V(shrn,      Shrn)                   \
    V(shrn2,     Shrn2)                  \
    V(sli,       Sli)                    \
    V(sqrshrn,   Sqrshrn)                \
    V(sqrshrn2,  Sqrshrn2)               \
    V(sqrshrun,  Sqrshrun)               \
    V(sqrshrun2, Sqrshrun2)              \
    V(sqshl,     Sqshl)                  \
    V(sqshlu,    Sqshlu)                 \
    V(sqshrn,    Sqshrn)                 \
    V(sqshrn2,   Sqshrn2)                \
    V(sqshrun,   Sqshrun)                \
    V(sqshrun2,  Sqshrun2)               \
    V(sri,       Sri)                    \
    V(srshr,     Srshr)                  \
    V(srsra,     Srsra)                  \
    V(sshll,     Sshll)                  \
    V(sshll2,    Sshll2)                 \
    V(sshr,      Sshr)                   \
    V(ssra,      Ssra)                   \
    V(uqrshrn,   Uqrshrn)                \
    V(uqrshrn2,  Uqrshrn2)               \
    V(uqshl,     Uqshl)                  \
    V(uqshrn,    Uqshrn)                 \
    V(uqshrn2,   Uqshrn2)                \
    V(urshr,     Urshr)                  \
    V(ursra,     Ursra)                  \
    V(ushll,     Ushll)                  \
    V(ushll2,    Ushll2)                 \
    V(ushr,      Ushr)                   \
    V(usra,      Usra)                   \

  #define DEFINE_MACRO_ASM_FUNC(ASM, MASM)   \
  void MASM(const VRegister& vd,             \
            const VRegister& vn,             \
            int shift) {                     \
    SingleEmissionCheckScope guard(this);    \
    ASM(vd, vn, shift);                      \
  }
  NEON_2VREG_SHIFT_MACRO_LIST(DEFINE_MACRO_ASM_FUNC)
  #undef DEFINE_MACRO_ASM_FUNC

  void Bic(const VRegister& vd,
           const int imm8,
           const int left_shift = 0) {
    SingleEmissionCheckScope guard(this);
    bic(vd, imm8, left_shift);
  }
  void Cmeq(const VRegister& vd,
            const VRegister& vn,
            int imm) {
    SingleEmissionCheckScope guard(this);
    cmeq(vd, vn, imm);
  }
  void Cmge(const VRegister& vd,
            const VRegister& vn,
            int imm) {
    SingleEmissionCheckScope guard(this);
    cmge(vd, vn, imm);
  }
  void Cmgt(const VRegister& vd,
            const VRegister& vn,
            int imm) {
    SingleEmissionCheckScope guard(this);
    cmgt(vd, vn, imm);
  }
  void Cmle(const VRegister& vd,
            const VRegister& vn,
            int imm) {
    SingleEmissionCheckScope guard(this);
    cmle(vd, vn, imm);
  }
  void Cmlt(const VRegister& vd,
            const VRegister& vn,
            int imm) {
    SingleEmissionCheckScope guard(this);
    cmlt(vd, vn, imm);
  }
  void Dup(const VRegister& vd,
           const VRegister& vn,
           int index) {
    SingleEmissionCheckScope guard(this);
    dup(vd, vn, index);
  }
  void Dup(const VRegister& vd,
           const Register& rn) {
    SingleEmissionCheckScope guard(this);
    dup(vd, rn);
  }
  void Ext(const VRegister& vd,
           const VRegister& vn,
           const VRegister& vm,
           int index) {
    SingleEmissionCheckScope guard(this);
    ext(vd, vn, vm, index);
  }
  void Ins(const VRegister& vd,
           int vd_index,
           const VRegister& vn,
           int vn_index) {
    SingleEmissionCheckScope guard(this);
    ins(vd, vd_index, vn, vn_index);
  }
  void Ins(const VRegister& vd,
           int vd_index,
           const Register& rn) {
    SingleEmissionCheckScope guard(this);
    ins(vd, vd_index, rn);
  }
  void Ld1(const VRegister& vt,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld1(vt, src);
  }
  void Ld1(const VRegister& vt,
           const VRegister& vt2,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld1(vt, vt2, src);
  }
  void Ld1(const VRegister& vt,
           const VRegister& vt2,
           const VRegister& vt3,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld1(vt, vt2, vt3, src);
  }
  void Ld1(const VRegister& vt,
           const VRegister& vt2,
           const VRegister& vt3,
           const VRegister& vt4,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld1(vt, vt2, vt3, vt4, src);
  }
  void Ld1(const VRegister& vt,
           int lane,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld1(vt, lane, src);
  }
  void Ld1r(const VRegister& vt,
            const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld1r(vt, src);
  }
  void Ld2(const VRegister& vt,
           const VRegister& vt2,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld2(vt, vt2, src);
  }
  void Ld2(const VRegister& vt,
           const VRegister& vt2,
           int lane,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld2(vt, vt2, lane, src);
  }
  void Ld2r(const VRegister& vt,
            const VRegister& vt2,
            const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld2r(vt, vt2, src);
  }
  void Ld3(const VRegister& vt,
           const VRegister& vt2,
           const VRegister& vt3,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld3(vt, vt2, vt3, src);
  }
  void Ld3(const VRegister& vt,
           const VRegister& vt2,
           const VRegister& vt3,
           int lane,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld3(vt, vt2, vt3, lane, src);
  }
  void Ld3r(const VRegister& vt,
            const VRegister& vt2,
            const VRegister& vt3,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld3r(vt, vt2, vt3, src);
  }
  void Ld4(const VRegister& vt,
           const VRegister& vt2,
           const VRegister& vt3,
           const VRegister& vt4,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld4(vt, vt2, vt3, vt4, src);
  }
  void Ld4(const VRegister& vt,
           const VRegister& vt2,
           const VRegister& vt3,
           const VRegister& vt4,
           int lane,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld4(vt, vt2, vt3, vt4, lane, src);
  }
  void Ld4r(const VRegister& vt,
            const VRegister& vt2,
            const VRegister& vt3,
            const VRegister& vt4,
           const MemOperand& src) {
    SingleEmissionCheckScope guard(this);
    ld4r(vt, vt2, vt3, vt4, src);
  }
  void Mov(const VRegister& vd,
           int vd_index,
           const VRegister& vn,
           int vn_index) {
    SingleEmissionCheckScope guard(this);
    mov(vd, vd_index, vn, vn_index);
  }
  void Mov(const VRegister& vd,
           const VRegister& vn,
           int index) {
    SingleEmissionCheckScope guard(this);
    mov(vd, vn, index);
  }
  void Mov(const VRegister& vd,
           int vd_index,
           const Register& rn) {
    SingleEmissionCheckScope guard(this);
    mov(vd, vd_index, rn);
  }
  void Mov(const Register& rd,
           const VRegister& vn,
           int vn_index) {
    SingleEmissionCheckScope guard(this);
    mov(rd, vn, vn_index);
  }
  void Movi(const VRegister& vd,
            uint64_t imm,
            Shift shift = LSL,
            int shift_amount = 0);
  void Movi(const VRegister& vd, uint64_t hi, uint64_t lo);
  void Mvni(const VRegister& vd,
            const int imm8,
            Shift shift = LSL,
            const int shift_amount = 0) {
    SingleEmissionCheckScope guard(this);
    mvni(vd, imm8, shift, shift_amount);
  }
  void Orr(const VRegister& vd,
           const int imm8,
           const int left_shift = 0) {
    SingleEmissionCheckScope guard(this);
    orr(vd, imm8, left_shift);
  }
  void Scvtf(const VRegister& vd,
             const VRegister& vn,
             int fbits = 0) {
    SingleEmissionCheckScope guard(this);
    scvtf(vd, vn, fbits);
  }
  void Ucvtf(const VRegister& vd,
             const VRegister& vn,
             int fbits = 0) {
    SingleEmissionCheckScope guard(this);
    ucvtf(vd, vn, fbits);
  }
  void Fcvtzs(const VRegister& vd,
              const VRegister& vn,
              int fbits = 0) {
    SingleEmissionCheckScope guard(this);
    fcvtzs(vd, vn, fbits);
  }
  void Fcvtzu(const VRegister& vd,
              const VRegister& vn,
              int fbits = 0) {
    SingleEmissionCheckScope guard(this);
    fcvtzu(vd, vn, fbits);
  }
  void St1(const VRegister& vt,
           const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    st1(vt, dst);
  }
  void St1(const VRegister& vt,
           const VRegister& vt2,
           const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    st1(vt, vt2, dst);
  }
  void St1(const VRegister& vt,
           const VRegister& vt2,
           const VRegister& vt3,
           const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    st1(vt, vt2, vt3, dst);
  }
  void St1(const VRegister& vt,
           const VRegister& vt2,
           const VRegister& vt3,
           const VRegister& vt4,
           const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    st1(vt, vt2, vt3, vt4, dst);
  }
  void St1(const VRegister& vt,
           int lane,
           const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    st1(vt, lane, dst);
  }
  void St2(const VRegister& vt,
           const VRegister& vt2,
           const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    st2(vt, vt2, dst);
  }
  void St3(const VRegister& vt,
           const VRegister& vt2,
           const VRegister& vt3,
           const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    st3(vt, vt2, vt3, dst);
  }
  void St4(const VRegister& vt,
           const VRegister& vt2,
           const VRegister& vt3,
           const VRegister& vt4,
           const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    st4(vt, vt2, vt3, vt4, dst);
  }
  void St2(const VRegister& vt,
           const VRegister& vt2,
           int lane,
           const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    st2(vt, vt2, lane, dst);
  }
  void St3(const VRegister& vt,
           const VRegister& vt2,
           const VRegister& vt3,
           int lane,
           const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    st3(vt, vt2, vt3, lane, dst);
  }
  void St4(const VRegister& vt,
           const VRegister& vt2,
           const VRegister& vt3,
           const VRegister& vt4,
           int lane,
           const MemOperand& dst) {
    SingleEmissionCheckScope guard(this);
    st4(vt, vt2, vt3, vt4, lane, dst);
  }
  void Smov(const Register& rd,
            const VRegister& vn,
            int vn_index) {
    SingleEmissionCheckScope guard(this);
    smov(rd, vn, vn_index);
  }
  void Umov(const Register& rd,
            const VRegister& vn,
            int vn_index) {
    SingleEmissionCheckScope guard(this);
    umov(rd, vn, vn_index);
  }
  void Crc32b(const Register& rd,
              const Register& rn,
              const Register& rm) {
    SingleEmissionCheckScope guard(this);
    crc32b(rd, rn, rm);
  }
  void Crc32h(const Register& rd,
              const Register& rn,
              const Register& rm) {
    SingleEmissionCheckScope guard(this);
    crc32h(rd, rn, rm);
  }
  void Crc32w(const Register& rd,
              const Register& rn,
              const Register& rm) {
    SingleEmissionCheckScope guard(this);
    crc32w(rd, rn, rm);
  }
  void Crc32x(const Register& rd,
              const Register& rn,
              const Register& rm) {
    SingleEmissionCheckScope guard(this);
    crc32x(rd, rn, rm);
  }
  void Crc32cb(const Register& rd,
               const Register& rn,
               const Register& rm) {
    SingleEmissionCheckScope guard(this);
    crc32cb(rd, rn, rm);
  }
  void Crc32ch(const Register& rd,
               const Register& rn,
               const Register& rm) {
    SingleEmissionCheckScope guard(this);
    crc32ch(rd, rn, rm);
  }
  void Crc32cw(const Register& rd,
               const Register& rn,
               const Register& rm) {
    SingleEmissionCheckScope guard(this);
    crc32cw(rd, rn, rm);
  }
  void Crc32cx(const Register& rd,
               const Register& rn,
               const Register& rm) {
    SingleEmissionCheckScope guard(this);
    crc32cx(rd, rn, rm);
  }

  // Push the system stack pointer (sp) down to allow the same to be done to
  // the current stack pointer (according to StackPointer()). This must be
  // called _before_ accessing the memory.
  //
  // This is necessary when pushing or otherwise adding things to the stack, to
  // satisfy the AAPCS64 constraint that the memory below the system stack
  // pointer is not accessed.
  //
  // This method asserts that StackPointer() is not sp, since the call does
  // not make sense in that context.
  //
  // TODO: This method can only accept values of 'space' that can be encoded in
  // one instruction. Refer to the implementation for details.
  void BumpSystemStackPointer(const Operand& space);

  // Set the current stack pointer, but don't generate any code.
  void SetStackPointer64(const Register& stack_pointer) {
    VIXL_ASSERT(!TmpList()->IncludesAliasOf(stack_pointer));
    sp_ = stack_pointer;
  }

  // Return the current stack pointer, as set by SetStackPointer.
  const Register& StackPointer() const {
    return sp_;
  }

  const Register& GetStackPointer64() const {
    return sp_;
  }

  js::jit::RegisterOrSP getStackPointer() const {
      return js::jit::RegisterOrSP(sp_.code());
  }

  CPURegList* TmpList() { return &tmp_list_; }
  CPURegList* FPTmpList() { return &fptmp_list_; }

  // Trace control when running the debug simulator.
  //
  // For example:
  //
  // __ Trace(LOG_REGS, TRACE_ENABLE);
  // Will add registers to the trace if it wasn't already the case.
  //
  // __ Trace(LOG_DISASM, TRACE_DISABLE);
  // Will stop logging disassembly. It has no effect if the disassembly wasn't
  // already being logged.
  void Trace(TraceParameters parameters, TraceCommand command);

  // Log the requested data independently of what is being traced.
  //
  // For example:
  //
  // __ Log(LOG_FLAGS)
  // Will output the flags.
  void Log(TraceParameters parameters);

  // Enable or disable instrumentation when an Instrument visitor is attached to
  // the simulator.
  void EnableInstrumentation();
  void DisableInstrumentation();

  // Add a marker to the instrumentation data produced by an Instrument visitor.
  // The name is a two character string that will be attached to the marker in
  // the output data.
  void AnnotateInstrumentation(const char* marker_name);

 private:
  // The actual Push and Pop implementations. These don't generate any code
  // other than that required for the push or pop. This allows
  // (Push|Pop)CPURegList to bundle together setup code for a large block of
  // registers.
  //
  // Note that size is per register, and is specified in bytes.
  void PushHelper(int count, int size,
                  const CPURegister& src0, const CPURegister& src1,
                  const CPURegister& src2, const CPURegister& src3);
  void PopHelper(int count, int size,
                 const CPURegister& dst0, const CPURegister& dst1,
                 const CPURegister& dst2, const CPURegister& dst3);

  void Movi16bitHelper(const VRegister& vd, uint64_t imm);
  void Movi32bitHelper(const VRegister& vd, uint64_t imm);
  void Movi64bitHelper(const VRegister& vd, uint64_t imm);

  // Perform necessary maintenance operations before a push or pop.
  //
  // Note that size is per register, and is specified in bytes.
  void PrepareForPush(int count, int size);
  void PrepareForPop(int count, int size);

  // The actual implementation of load and store operations for CPURegList.
  enum LoadStoreCPURegListAction {
    kLoad,
    kStore
  };
  void LoadStoreCPURegListHelper(LoadStoreCPURegListAction operation,
                                 CPURegList registers,
                                 const MemOperand& mem);
  // Returns a MemOperand suitable for loading or storing a CPURegList at `dst`.
  // This helper may allocate registers from `scratch_scope` and generate code
  // to compute an intermediate address. The resulting MemOperand is only valid
  // as long as `scratch_scope` remains valid.
  MemOperand BaseMemOperandForLoadStoreCPURegList(
      const CPURegList& registers,
      const MemOperand& mem,
      UseScratchRegisterScope* scratch_scope);

  bool LabelIsOutOfRange(Label* label, ImmBranchType branch_type) {
    return !Instruction::IsValidImmPCOffset(branch_type, nextOffset().getOffset() - label->offset());
  }

  // The register to use as a stack pointer for stack operations.
  Register sp_;

  // Scratch registers available for use by the MacroAssembler.
  CPURegList tmp_list_;
  CPURegList fptmp_list_;

  ptrdiff_t checkpoint_;
  ptrdiff_t recommended_checkpoint_;
};


// All Assembler emits MUST acquire/release the underlying code buffer. The
// helper scope below will do so and optionally ensure the buffer is big enough
// to receive the emit. It is possible to request the scope not to perform any
// checks (kNoCheck) if for example it is known in advance the buffer size is
// adequate or there is some other size checking mechanism in place.
class CodeBufferCheckScope {
 public:
  // Tell whether or not the scope needs to ensure the associated CodeBuffer
  // has enough space for the requested size.
  enum CheckPolicy {
    kNoCheck,
    kCheck
  };

  // Tell whether or not the scope should assert the amount of code emitted
  // within the scope is consistent with the requested amount.
  enum AssertPolicy {
    kNoAssert,    // No assert required.
    kExactSize,   // The code emitted must be exactly size bytes.
    kMaximumSize  // The code emitted must be at most size bytes.
  };

  CodeBufferCheckScope(Assembler* assm,
                       size_t size,
                       CheckPolicy check_policy = kCheck,
                       AssertPolicy assert_policy = kMaximumSize)
  { }

  // This is a shortcut for CodeBufferCheckScope(assm, 0, kNoCheck, kNoAssert).
  explicit CodeBufferCheckScope(Assembler* assm) {}
};


// Use this scope when you need a one-to-one mapping between methods and
// instructions. This scope prevents the MacroAssembler from being called and
// literal pools from being emitted. It also asserts the number of instructions
// emitted is what you specified when creating the scope.
// FIXME: Because of the disabled calls below, this class asserts nothing.
class InstructionAccurateScope : public CodeBufferCheckScope {
 public:
  InstructionAccurateScope(MacroAssembler* masm,
                           int64_t count,
                           AssertPolicy policy = kExactSize)
      : CodeBufferCheckScope(masm,
                             (count * kInstructionSize),
                             kCheck,
                             policy) {
  }
};


// This scope utility allows scratch registers to be managed safely. The
// MacroAssembler's TmpList() (and FPTmpList()) is used as a pool of scratch
// registers. These registers can be allocated on demand, and will be returned
// at the end of the scope.
//
// When the scope ends, the MacroAssembler's lists will be restored to their
// original state, even if the lists were modified by some other means.
class UseScratchRegisterScope {
 public:
  // This constructor implicitly calls the `Open` function to initialise the
  // scope, so it is ready to use immediately after it has been constructed.
  explicit UseScratchRegisterScope(MacroAssembler* masm);
  // This constructor allows deferred and optional initialisation of the scope.
  // The user is required to explicitly call the `Open` function before using
  // the scope.
  UseScratchRegisterScope();
  // This function performs the actual initialisation work.
  void Open(MacroAssembler* masm);

  // The destructor always implicitly calls the `Close` function.
  ~UseScratchRegisterScope();
  // This function performs the cleaning-up work. It must succeed even if the
  // scope has not been opened. It is safe to call multiple times.
  void Close();


  bool IsAvailable(const CPURegister& reg) const;


  // Take a register from the appropriate temps list. It will be returned
  // automatically when the scope ends.
  Register AcquireW() { return AcquireNextAvailable(available_).W(); }
  Register AcquireX() { return AcquireNextAvailable(available_).X(); }
  VRegister AcquireS() { return AcquireNextAvailable(availablefp_).S(); }
  VRegister AcquireD() { return AcquireNextAvailable(availablefp_).D(); }
  VRegister AcquireQ() { return AcquireNextAvailable(availablefp_).Q(); }


  Register AcquireSameSizeAs(const Register& reg);
  VRegister AcquireSameSizeAs(const VRegister& reg);


  // Explicitly release an acquired (or excluded) register, putting it back in
  // the appropriate temps list.
  void Release(const CPURegister& reg);


  // Make the specified registers available as scratch registers for the
  // duration of this scope.
  void Include(const CPURegList& list);
  void Include(const Register& reg1,
               const Register& reg2 = NoReg,
               const Register& reg3 = NoReg,
               const Register& reg4 = NoReg);
  void Include(const VRegister& reg1,
               const VRegister& reg2 = NoVReg,
               const VRegister& reg3 = NoVReg,
               const VRegister& reg4 = NoVReg);


  // Make sure that the specified registers are not available in this scope.
  // This can be used to prevent helper functions from using sensitive
  // registers, for example.
  void Exclude(const CPURegList& list);
  void Exclude(const Register& reg1,
               const Register& reg2 = NoReg,
               const Register& reg3 = NoReg,
               const Register& reg4 = NoReg);
  void Exclude(const VRegister& reg1,
               const VRegister& reg2 = NoVReg,
               const VRegister& reg3 = NoVReg,
               const VRegister& reg4 = NoVReg);
  void Exclude(const CPURegister& reg1,
               const CPURegister& reg2 = NoCPUReg,
               const CPURegister& reg3 = NoCPUReg,
               const CPURegister& reg4 = NoCPUReg);


  // Prevent any scratch registers from being used in this scope.
  void ExcludeAll();


 private:
  static CPURegister AcquireNextAvailable(CPURegList* available);

  static void ReleaseByCode(CPURegList* available, int code);

  static void ReleaseByRegList(CPURegList* available,
                               RegList regs);

  static void IncludeByRegList(CPURegList* available,
                               RegList exclude);

  static void ExcludeByRegList(CPURegList* available,
                               RegList exclude);

  // Available scratch registers.
  CPURegList* available_;     // kRegister
  CPURegList* availablefp_;   // kVRegister

  // The state of the available lists at the start of this scope.
  RegList old_available_;     // kRegister
  RegList old_availablefp_;   // kVRegister
#ifdef DEBUG
  bool initialised_;
#endif

  // Disallow copy constructor and operator=.
  UseScratchRegisterScope(const UseScratchRegisterScope&) {
    VIXL_UNREACHABLE();
  }
  void operator=(const UseScratchRegisterScope&) {
    VIXL_UNREACHABLE();
  }
};


}  // namespace vixl

#endif  // VIXL_A64_MACRO_ASSEMBLER_A64_H_
