C++程序  |  1668行  |  43.32 KB

// Copyright 2013 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_ARM64_MACRO_ASSEMBLER_ARM64_INL_H_
#define V8_ARM64_MACRO_ASSEMBLER_ARM64_INL_H_

#include <ctype.h>

#include "src/globals.h"

#include "src/arm64/assembler-arm64-inl.h"
#include "src/arm64/assembler-arm64.h"
#include "src/arm64/instrument-arm64.h"
#include "src/arm64/macro-assembler-arm64.h"
#include "src/base/bits.h"


namespace v8 {
namespace internal {


MemOperand FieldMemOperand(Register object, int offset) {
  return MemOperand(object, offset - kHeapObjectTag);
}


MemOperand UntagSmiFieldMemOperand(Register object, int offset) {
  return UntagSmiMemOperand(object, offset - kHeapObjectTag);
}


MemOperand UntagSmiMemOperand(Register object, int offset) {
  // Assumes that Smis are shifted by 32 bits and little endianness.
  STATIC_ASSERT(kSmiShift == 32);
  return MemOperand(object, offset + (kSmiShift / kBitsPerByte));
}


Handle<Object> MacroAssembler::CodeObject() {
  DCHECK(!code_object_.is_null());
  return code_object_;
}


void MacroAssembler::And(const Register& rd,
                         const Register& rn,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  LogicalMacro(rd, rn, operand, AND);
}


void MacroAssembler::Ands(const Register& rd,
                          const Register& rn,
                          const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  LogicalMacro(rd, rn, operand, ANDS);
}


void MacroAssembler::Tst(const Register& rn,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  LogicalMacro(AppropriateZeroRegFor(rn), rn, operand, ANDS);
}


void MacroAssembler::Bic(const Register& rd,
                         const Register& rn,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  LogicalMacro(rd, rn, operand, BIC);
}


void MacroAssembler::Bics(const Register& rd,
                          const Register& rn,
                          const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  LogicalMacro(rd, rn, operand, BICS);
}


void MacroAssembler::Orr(const Register& rd,
                         const Register& rn,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  LogicalMacro(rd, rn, operand, ORR);
}


void MacroAssembler::Orn(const Register& rd,
                         const Register& rn,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  LogicalMacro(rd, rn, operand, ORN);
}


void MacroAssembler::Eor(const Register& rd,
                         const Register& rn,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  LogicalMacro(rd, rn, operand, EOR);
}


void MacroAssembler::Eon(const Register& rd,
                         const Register& rn,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  LogicalMacro(rd, rn, operand, EON);
}


void MacroAssembler::Ccmp(const Register& rn,
                          const Operand& operand,
                          StatusFlags nzcv,
                          Condition cond) {
  DCHECK(allow_macro_instructions_);
  if (operand.IsImmediate() && (operand.ImmediateValue() < 0)) {
    ConditionalCompareMacro(rn, -operand.ImmediateValue(), nzcv, cond, CCMN);
  } else {
    ConditionalCompareMacro(rn, operand, nzcv, cond, CCMP);
  }
}


void MacroAssembler::Ccmn(const Register& rn,
                          const Operand& operand,
                          StatusFlags nzcv,
                          Condition cond) {
  DCHECK(allow_macro_instructions_);
  if (operand.IsImmediate() && (operand.ImmediateValue() < 0)) {
    ConditionalCompareMacro(rn, -operand.ImmediateValue(), nzcv, cond, CCMP);
  } else {
    ConditionalCompareMacro(rn, operand, nzcv, cond, CCMN);
  }
}


void MacroAssembler::Add(const Register& rd,
                         const Register& rn,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  if (operand.IsImmediate() && (operand.ImmediateValue() < 0) &&
      IsImmAddSub(-operand.ImmediateValue())) {
    AddSubMacro(rd, rn, -operand.ImmediateValue(), LeaveFlags, SUB);
  } else {
    AddSubMacro(rd, rn, operand, LeaveFlags, ADD);
  }
}

void MacroAssembler::Adds(const Register& rd,
                          const Register& rn,
                          const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  if (operand.IsImmediate() && (operand.ImmediateValue() < 0) &&
      IsImmAddSub(-operand.ImmediateValue())) {
    AddSubMacro(rd, rn, -operand.ImmediateValue(), SetFlags, SUB);
  } else {
    AddSubMacro(rd, rn, operand, SetFlags, ADD);
  }
}


void MacroAssembler::Sub(const Register& rd,
                         const Register& rn,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  if (operand.IsImmediate() && (operand.ImmediateValue() < 0) &&
      IsImmAddSub(-operand.ImmediateValue())) {
    AddSubMacro(rd, rn, -operand.ImmediateValue(), LeaveFlags, ADD);
  } else {
    AddSubMacro(rd, rn, operand, LeaveFlags, SUB);
  }
}


void MacroAssembler::Subs(const Register& rd,
                          const Register& rn,
                          const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  if (operand.IsImmediate() && (operand.ImmediateValue() < 0) &&
      IsImmAddSub(-operand.ImmediateValue())) {
    AddSubMacro(rd, rn, -operand.ImmediateValue(), SetFlags, ADD);
  } else {
    AddSubMacro(rd, rn, operand, SetFlags, SUB);
  }
}


void MacroAssembler::Cmn(const Register& rn, const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  Adds(AppropriateZeroRegFor(rn), rn, operand);
}


void MacroAssembler::Cmp(const Register& rn, const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  Subs(AppropriateZeroRegFor(rn), rn, operand);
}


void MacroAssembler::Neg(const Register& rd,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  if (operand.IsImmediate()) {
    Mov(rd, -operand.ImmediateValue());
  } else {
    Sub(rd, AppropriateZeroRegFor(rd), operand);
  }
}


void MacroAssembler::Negs(const Register& rd,
                          const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  Subs(rd, AppropriateZeroRegFor(rd), operand);
}


void MacroAssembler::Adc(const Register& rd,
                         const Register& rn,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  AddSubWithCarryMacro(rd, rn, operand, LeaveFlags, ADC);
}


void MacroAssembler::Adcs(const Register& rd,
                          const Register& rn,
                          const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  AddSubWithCarryMacro(rd, rn, operand, SetFlags, ADC);
}


void MacroAssembler::Sbc(const Register& rd,
                         const Register& rn,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  AddSubWithCarryMacro(rd, rn, operand, LeaveFlags, SBC);
}


void MacroAssembler::Sbcs(const Register& rd,
                          const Register& rn,
                          const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  AddSubWithCarryMacro(rd, rn, operand, SetFlags, SBC);
}


void MacroAssembler::Ngc(const Register& rd,
                         const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  Register zr = AppropriateZeroRegFor(rd);
  Sbc(rd, zr, operand);
}


void MacroAssembler::Ngcs(const Register& rd,
                          const Operand& operand) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  Register zr = AppropriateZeroRegFor(rd);
  Sbcs(rd, zr, operand);
}


void MacroAssembler::Mvn(const Register& rd, uint64_t imm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  Mov(rd, ~imm);
}


#define DEFINE_FUNCTION(FN, REGTYPE, REG, OP)                         \
void MacroAssembler::FN(const REGTYPE REG, const MemOperand& addr) {  \
  DCHECK(allow_macro_instructions_);                                  \
  LoadStoreMacro(REG, addr, OP);                                      \
}
LS_MACRO_LIST(DEFINE_FUNCTION)
#undef DEFINE_FUNCTION


#define DEFINE_FUNCTION(FN, REGTYPE, REG, REG2, OP)              \
  void MacroAssembler::FN(const REGTYPE REG, const REGTYPE REG2, \
                          const MemOperand& addr) {              \
    DCHECK(allow_macro_instructions_);                           \
    LoadStorePairMacro(REG, REG2, addr, OP);                     \
  }
LSPAIR_MACRO_LIST(DEFINE_FUNCTION)
#undef DEFINE_FUNCTION


void MacroAssembler::Asr(const Register& rd,
                         const Register& rn,
                         unsigned shift) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  asr(rd, rn, shift);
}


void MacroAssembler::Asr(const Register& rd,
                         const Register& rn,
                         const Register& rm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  asrv(rd, rn, rm);
}


void MacroAssembler::B(Label* label) {
  b(label);
  CheckVeneerPool(false, false);
}


void MacroAssembler::B(Condition cond, Label* label) {
  DCHECK(allow_macro_instructions_);
  B(label, cond);
}


void MacroAssembler::Bfi(const Register& rd,
                         const Register& rn,
                         unsigned lsb,
                         unsigned width) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  bfi(rd, rn, lsb, width);
}


void MacroAssembler::Bfxil(const Register& rd,
                           const Register& rn,
                           unsigned lsb,
                           unsigned width) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  bfxil(rd, rn, lsb, width);
}


void MacroAssembler::Bind(Label* label) {
  DCHECK(allow_macro_instructions_);
  bind(label);
}


void MacroAssembler::Bl(Label* label) {
  DCHECK(allow_macro_instructions_);
  bl(label);
}


void MacroAssembler::Blr(const Register& xn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!xn.IsZero());
  blr(xn);
}


void MacroAssembler::Br(const Register& xn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!xn.IsZero());
  br(xn);
}


void MacroAssembler::Brk(int code) {
  DCHECK(allow_macro_instructions_);
  brk(code);
}


void MacroAssembler::Cinc(const Register& rd,
                          const Register& rn,
                          Condition cond) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  DCHECK((cond != al) && (cond != nv));
  cinc(rd, rn, cond);
}


void MacroAssembler::Cinv(const Register& rd,
                          const Register& rn,
                          Condition cond) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  DCHECK((cond != al) && (cond != nv));
  cinv(rd, rn, cond);
}


void MacroAssembler::Cls(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  cls(rd, rn);
}


void MacroAssembler::Clz(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  clz(rd, rn);
}


void MacroAssembler::Cneg(const Register& rd,
                          const Register& rn,
                          Condition cond) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  DCHECK((cond != al) && (cond != nv));
  cneg(rd, rn, cond);
}


// Conditionally zero the destination register. Only X registers are supported
// due to the truncation side-effect when used on W registers.
void MacroAssembler::CzeroX(const Register& rd,
                            Condition cond) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsSP() && rd.Is64Bits());
  DCHECK((cond != al) && (cond != nv));
  csel(rd, xzr, rd, cond);
}


// Conditionally move a value into the destination register. Only X registers
// are supported due to the truncation side-effect when used on W registers.
void MacroAssembler::CmovX(const Register& rd,
                           const Register& rn,
                           Condition cond) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsSP());
  DCHECK(rd.Is64Bits() && rn.Is64Bits());
  DCHECK((cond != al) && (cond != nv));
  if (!rd.is(rn)) {
    csel(rd, rn, rd, cond);
  }
}


void MacroAssembler::Cset(const Register& rd, Condition cond) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  DCHECK((cond != al) && (cond != nv));
  cset(rd, cond);
}


void MacroAssembler::Csetm(const Register& rd, Condition cond) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  DCHECK((cond != al) && (cond != nv));
  csetm(rd, cond);
}


void MacroAssembler::Csinc(const Register& rd,
                           const Register& rn,
                           const Register& rm,
                           Condition cond) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  DCHECK((cond != al) && (cond != nv));
  csinc(rd, rn, rm, cond);
}


void MacroAssembler::Csinv(const Register& rd,
                           const Register& rn,
                           const Register& rm,
                           Condition cond) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  DCHECK((cond != al) && (cond != nv));
  csinv(rd, rn, rm, cond);
}


void MacroAssembler::Csneg(const Register& rd,
                           const Register& rn,
                           const Register& rm,
                           Condition cond) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  DCHECK((cond != al) && (cond != nv));
  csneg(rd, rn, rm, cond);
}


void MacroAssembler::Dmb(BarrierDomain domain, BarrierType type) {
  DCHECK(allow_macro_instructions_);
  dmb(domain, type);
}


void MacroAssembler::Dsb(BarrierDomain domain, BarrierType type) {
  DCHECK(allow_macro_instructions_);
  dsb(domain, type);
}


void MacroAssembler::Debug(const char* message, uint32_t code, Instr params) {
  DCHECK(allow_macro_instructions_);
  debug(message, code, params);
}


void MacroAssembler::Extr(const Register& rd,
                          const Register& rn,
                          const Register& rm,
                          unsigned lsb) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  extr(rd, rn, rm, lsb);
}


void MacroAssembler::Fabs(const FPRegister& fd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  fabs(fd, fn);
}


void MacroAssembler::Fadd(const FPRegister& fd,
                          const FPRegister& fn,
                          const FPRegister& fm) {
  DCHECK(allow_macro_instructions_);
  fadd(fd, fn, fm);
}


void MacroAssembler::Fccmp(const FPRegister& fn,
                           const FPRegister& fm,
                           StatusFlags nzcv,
                           Condition cond) {
  DCHECK(allow_macro_instructions_);
  DCHECK((cond != al) && (cond != nv));
  fccmp(fn, fm, nzcv, cond);
}


void MacroAssembler::Fcmp(const FPRegister& fn, const FPRegister& fm) {
  DCHECK(allow_macro_instructions_);
  fcmp(fn, fm);
}


void MacroAssembler::Fcmp(const FPRegister& fn, double value) {
  DCHECK(allow_macro_instructions_);
  if (value != 0.0) {
    UseScratchRegisterScope temps(this);
    FPRegister tmp = temps.AcquireSameSizeAs(fn);
    Fmov(tmp, value);
    fcmp(fn, tmp);
  } else {
    fcmp(fn, value);
  }
}


void MacroAssembler::Fcsel(const FPRegister& fd,
                           const FPRegister& fn,
                           const FPRegister& fm,
                           Condition cond) {
  DCHECK(allow_macro_instructions_);
  DCHECK((cond != al) && (cond != nv));
  fcsel(fd, fn, fm, cond);
}


void MacroAssembler::Fcvt(const FPRegister& fd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  fcvt(fd, fn);
}


void MacroAssembler::Fcvtas(const Register& rd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  fcvtas(rd, fn);
}


void MacroAssembler::Fcvtau(const Register& rd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  fcvtau(rd, fn);
}


void MacroAssembler::Fcvtms(const Register& rd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  fcvtms(rd, fn);
}


void MacroAssembler::Fcvtmu(const Register& rd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  fcvtmu(rd, fn);
}


void MacroAssembler::Fcvtns(const Register& rd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  fcvtns(rd, fn);
}


void MacroAssembler::Fcvtnu(const Register& rd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  fcvtnu(rd, fn);
}


void MacroAssembler::Fcvtzs(const Register& rd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  fcvtzs(rd, fn);
}
void MacroAssembler::Fcvtzu(const Register& rd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  fcvtzu(rd, fn);
}


void MacroAssembler::Fdiv(const FPRegister& fd,
                          const FPRegister& fn,
                          const FPRegister& fm) {
  DCHECK(allow_macro_instructions_);
  fdiv(fd, fn, fm);
}


void MacroAssembler::Fmadd(const FPRegister& fd,
                           const FPRegister& fn,
                           const FPRegister& fm,
                           const FPRegister& fa) {
  DCHECK(allow_macro_instructions_);
  fmadd(fd, fn, fm, fa);
}


void MacroAssembler::Fmax(const FPRegister& fd,
                          const FPRegister& fn,
                          const FPRegister& fm) {
  DCHECK(allow_macro_instructions_);
  fmax(fd, fn, fm);
}


void MacroAssembler::Fmaxnm(const FPRegister& fd,
                            const FPRegister& fn,
                            const FPRegister& fm) {
  DCHECK(allow_macro_instructions_);
  fmaxnm(fd, fn, fm);
}


void MacroAssembler::Fmin(const FPRegister& fd,
                          const FPRegister& fn,
                          const FPRegister& fm) {
  DCHECK(allow_macro_instructions_);
  fmin(fd, fn, fm);
}


void MacroAssembler::Fminnm(const FPRegister& fd,
                            const FPRegister& fn,
                            const FPRegister& fm) {
  DCHECK(allow_macro_instructions_);
  fminnm(fd, fn, fm);
}


void MacroAssembler::Fmov(FPRegister fd, FPRegister fn) {
  DCHECK(allow_macro_instructions_);
  // Only emit an instruction if fd and fn 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 FPRegister does not currently support Q registers.
  if (!fd.Is(fn) || !fd.Is64Bits()) {
    fmov(fd, fn);
  }
}


void MacroAssembler::Fmov(FPRegister fd, Register rn) {
  DCHECK(allow_macro_instructions_);
  fmov(fd, rn);
}


void MacroAssembler::Fmov(FPRegister fd, double imm) {
  DCHECK(allow_macro_instructions_);
  if (fd.Is32Bits()) {
    Fmov(fd, static_cast<float>(imm));
    return;
  }

  DCHECK(fd.Is64Bits());
  if (IsImmFP64(imm)) {
    fmov(fd, imm);
  } else if ((imm == 0.0) && (copysign(1.0, imm) == 1.0)) {
    fmov(fd, xzr);
  } else {
    Ldr(fd, imm);
  }
}


void MacroAssembler::Fmov(FPRegister fd, float imm) {
  DCHECK(allow_macro_instructions_);
  if (fd.Is64Bits()) {
    Fmov(fd, static_cast<double>(imm));
    return;
  }

  DCHECK(fd.Is32Bits());
  if (IsImmFP32(imm)) {
    fmov(fd, imm);
  } else if ((imm == 0.0) && (copysign(1.0, imm) == 1.0)) {
    fmov(fd, wzr);
  } else {
    UseScratchRegisterScope temps(this);
    Register tmp = temps.AcquireW();
    // TODO(all): Use Assembler::ldr(const FPRegister& ft, float imm).
    Mov(tmp, float_to_rawbits(imm));
    Fmov(fd, tmp);
  }
}


void MacroAssembler::Fmov(Register rd, FPRegister fn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  fmov(rd, fn);
}


void MacroAssembler::Fmsub(const FPRegister& fd,
                           const FPRegister& fn,
                           const FPRegister& fm,
                           const FPRegister& fa) {
  DCHECK(allow_macro_instructions_);
  fmsub(fd, fn, fm, fa);
}


void MacroAssembler::Fmul(const FPRegister& fd,
                          const FPRegister& fn,
                          const FPRegister& fm) {
  DCHECK(allow_macro_instructions_);
  fmul(fd, fn, fm);
}


void MacroAssembler::Fneg(const FPRegister& fd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  fneg(fd, fn);
}


void MacroAssembler::Fnmadd(const FPRegister& fd,
                            const FPRegister& fn,
                            const FPRegister& fm,
                            const FPRegister& fa) {
  DCHECK(allow_macro_instructions_);
  fnmadd(fd, fn, fm, fa);
}


void MacroAssembler::Fnmsub(const FPRegister& fd,
                            const FPRegister& fn,
                            const FPRegister& fm,
                            const FPRegister& fa) {
  DCHECK(allow_macro_instructions_);
  fnmsub(fd, fn, fm, fa);
}


void MacroAssembler::Frinta(const FPRegister& fd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  frinta(fd, fn);
}


void MacroAssembler::Frintm(const FPRegister& fd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  frintm(fd, fn);
}


void MacroAssembler::Frintn(const FPRegister& fd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  frintn(fd, fn);
}


void MacroAssembler::Frintp(const FPRegister& fd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  frintp(fd, fn);
}


void MacroAssembler::Frintz(const FPRegister& fd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  frintz(fd, fn);
}


void MacroAssembler::Fsqrt(const FPRegister& fd, const FPRegister& fn) {
  DCHECK(allow_macro_instructions_);
  fsqrt(fd, fn);
}


void MacroAssembler::Fsub(const FPRegister& fd,
                          const FPRegister& fn,
                          const FPRegister& fm) {
  DCHECK(allow_macro_instructions_);
  fsub(fd, fn, fm);
}


void MacroAssembler::Hint(SystemHint code) {
  DCHECK(allow_macro_instructions_);
  hint(code);
}


void MacroAssembler::Hlt(int code) {
  DCHECK(allow_macro_instructions_);
  hlt(code);
}


void MacroAssembler::Isb() {
  DCHECK(allow_macro_instructions_);
  isb();
}


void MacroAssembler::Ldr(const CPURegister& rt, const Immediate& imm) {
  DCHECK(allow_macro_instructions_);
  ldr(rt, imm);
}


void MacroAssembler::Ldr(const CPURegister& rt, double imm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(rt.Is64Bits());
  ldr(rt, Immediate(double_to_rawbits(imm)));
}


void MacroAssembler::Lsl(const Register& rd,
                         const Register& rn,
                         unsigned shift) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  lsl(rd, rn, shift);
}


void MacroAssembler::Lsl(const Register& rd,
                         const Register& rn,
                         const Register& rm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  lslv(rd, rn, rm);
}


void MacroAssembler::Lsr(const Register& rd,
                         const Register& rn,
                         unsigned shift) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  lsr(rd, rn, shift);
}


void MacroAssembler::Lsr(const Register& rd,
                         const Register& rn,
                         const Register& rm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  lsrv(rd, rn, rm);
}


void MacroAssembler::Madd(const Register& rd,
                          const Register& rn,
                          const Register& rm,
                          const Register& ra) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  madd(rd, rn, rm, ra);
}


void MacroAssembler::Mneg(const Register& rd,
                          const Register& rn,
                          const Register& rm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  mneg(rd, rn, rm);
}


void MacroAssembler::Mov(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  // Emit a register move only if the registers are distinct, or if they are
  // not X registers. Note that mov(w0, w0) is not a no-op because it clears
  // the top word of x0.
  if (!rd.Is(rn) || !rd.Is64Bits()) {
    Assembler::mov(rd, rn);
  }
}


void MacroAssembler::Movk(const Register& rd, uint64_t imm, int shift) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  movk(rd, imm, shift);
}


void MacroAssembler::Mrs(const Register& rt, SystemRegister sysreg) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rt.IsZero());
  mrs(rt, sysreg);
}


void MacroAssembler::Msr(SystemRegister sysreg, const Register& rt) {
  DCHECK(allow_macro_instructions_);
  msr(sysreg, rt);
}


void MacroAssembler::Msub(const Register& rd,
                          const Register& rn,
                          const Register& rm,
                          const Register& ra) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  msub(rd, rn, rm, ra);
}


void MacroAssembler::Mul(const Register& rd,
                         const Register& rn,
                         const Register& rm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  mul(rd, rn, rm);
}


void MacroAssembler::Rbit(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  rbit(rd, rn);
}


void MacroAssembler::Ret(const Register& xn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!xn.IsZero());
  ret(xn);
  CheckVeneerPool(false, false);
}


void MacroAssembler::Rev(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  rev(rd, rn);
}


void MacroAssembler::Rev16(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  rev16(rd, rn);
}


void MacroAssembler::Rev32(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  rev32(rd, rn);
}


void MacroAssembler::Ror(const Register& rd,
                         const Register& rs,
                         unsigned shift) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  ror(rd, rs, shift);
}


void MacroAssembler::Ror(const Register& rd,
                         const Register& rn,
                         const Register& rm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  rorv(rd, rn, rm);
}


void MacroAssembler::Sbfiz(const Register& rd,
                           const Register& rn,
                           unsigned lsb,
                           unsigned width) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  sbfiz(rd, rn, lsb, width);
}


void MacroAssembler::Sbfx(const Register& rd,
                          const Register& rn,
                          unsigned lsb,
                          unsigned width) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  sbfx(rd, rn, lsb, width);
}


void MacroAssembler::Scvtf(const FPRegister& fd,
                           const Register& rn,
                           unsigned fbits) {
  DCHECK(allow_macro_instructions_);
  scvtf(fd, rn, fbits);
}


void MacroAssembler::Sdiv(const Register& rd,
                          const Register& rn,
                          const Register& rm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  sdiv(rd, rn, rm);
}


void MacroAssembler::Smaddl(const Register& rd,
                            const Register& rn,
                            const Register& rm,
                            const Register& ra) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  smaddl(rd, rn, rm, ra);
}


void MacroAssembler::Smsubl(const Register& rd,
                            const Register& rn,
                            const Register& rm,
                            const Register& ra) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  smsubl(rd, rn, rm, ra);
}


void MacroAssembler::Smull(const Register& rd,
                           const Register& rn,
                           const Register& rm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  smull(rd, rn, rm);
}


void MacroAssembler::Smulh(const Register& rd,
                           const Register& rn,
                           const Register& rm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  smulh(rd, rn, rm);
}


void MacroAssembler::Umull(const Register& rd, const Register& rn,
                           const Register& rm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  umaddl(rd, rn, rm, xzr);
}


void MacroAssembler::Sxtb(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  sxtb(rd, rn);
}


void MacroAssembler::Sxth(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  sxth(rd, rn);
}


void MacroAssembler::Sxtw(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  sxtw(rd, rn);
}


void MacroAssembler::Ubfiz(const Register& rd,
                           const Register& rn,
                           unsigned lsb,
                           unsigned width) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  ubfiz(rd, rn, lsb, width);
}


void MacroAssembler::Ubfx(const Register& rd,
                          const Register& rn,
                          unsigned lsb,
                          unsigned width) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  ubfx(rd, rn, lsb, width);
}


void MacroAssembler::Ucvtf(const FPRegister& fd,
                           const Register& rn,
                           unsigned fbits) {
  DCHECK(allow_macro_instructions_);
  ucvtf(fd, rn, fbits);
}


void MacroAssembler::Udiv(const Register& rd,
                          const Register& rn,
                          const Register& rm) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  udiv(rd, rn, rm);
}


void MacroAssembler::Umaddl(const Register& rd,
                            const Register& rn,
                            const Register& rm,
                            const Register& ra) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  umaddl(rd, rn, rm, ra);
}


void MacroAssembler::Umsubl(const Register& rd,
                            const Register& rn,
                            const Register& rm,
                            const Register& ra) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  umsubl(rd, rn, rm, ra);
}


void MacroAssembler::Uxtb(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  uxtb(rd, rn);
}


void MacroAssembler::Uxth(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  uxth(rd, rn);
}


void MacroAssembler::Uxtw(const Register& rd, const Register& rn) {
  DCHECK(allow_macro_instructions_);
  DCHECK(!rd.IsZero());
  uxtw(rd, rn);
}


void MacroAssembler::BumpSystemStackPointer(const Operand& space) {
  DCHECK(!csp.Is(sp_));
  if (!TmpList()->IsEmpty()) {
    Sub(csp, StackPointer(), space);
  } else {
    // TODO(jbramley): Several callers rely on this not using scratch
    // registers, so we use the assembler directly here. However, this means
    // that large immediate values of 'space' cannot be handled cleanly. (Only
    // 24-bits immediates or values of 'space' that can be encoded in one
    // instruction are accepted.) Once we implement our flexible scratch
    // register idea, we could greatly simplify this function.
    InstructionAccurateScope scope(this);
    DCHECK(space.IsImmediate());
    // Align to 16 bytes.
    uint64_t imm = RoundUp(space.ImmediateValue(), 0x10);
    DCHECK(is_uint24(imm));

    Register source = StackPointer();
    if (CpuFeatures::IsSupported(ALWAYS_ALIGN_CSP)) {
      bic(csp, source, 0xf);
      source = csp;
    }
    if (!is_uint12(imm)) {
      int64_t imm_top_12_bits = imm >> 12;
      sub(csp, source, imm_top_12_bits << 12);
      source = csp;
      imm -= imm_top_12_bits << 12;
    }
    if (imm > 0) {
      sub(csp, source, imm);
    }
  }
  AssertStackConsistency();
}


void MacroAssembler::SyncSystemStackPointer() {
  DCHECK(emit_debug_code());
  DCHECK(!csp.Is(sp_));
  { InstructionAccurateScope scope(this);
    mov(csp, StackPointer());
  }
  AssertStackConsistency();
}


void MacroAssembler::InitializeRootRegister() {
  ExternalReference roots_array_start =
      ExternalReference::roots_array_start(isolate());
  Mov(root, Operand(roots_array_start));
}


void MacroAssembler::SmiTag(Register dst, Register src) {
  STATIC_ASSERT(kXRegSizeInBits ==
                static_cast<unsigned>(kSmiShift + kSmiValueSize));
  DCHECK(dst.Is64Bits() && src.Is64Bits());
  Lsl(dst, src, kSmiShift);
}


void MacroAssembler::SmiTag(Register smi) { SmiTag(smi, smi); }


void MacroAssembler::SmiUntag(Register dst, Register src) {
  STATIC_ASSERT(kXRegSizeInBits ==
                static_cast<unsigned>(kSmiShift + kSmiValueSize));
  DCHECK(dst.Is64Bits() && src.Is64Bits());
  if (FLAG_enable_slow_asserts) {
    AssertSmi(src);
  }
  Asr(dst, src, kSmiShift);
}


void MacroAssembler::SmiUntag(Register smi) { SmiUntag(smi, smi); }


void MacroAssembler::SmiUntagToDouble(FPRegister dst,
                                      Register src,
                                      UntagMode mode) {
  DCHECK(dst.Is64Bits() && src.Is64Bits());
  if (FLAG_enable_slow_asserts && (mode == kNotSpeculativeUntag)) {
    AssertSmi(src);
  }
  Scvtf(dst, src, kSmiShift);
}


void MacroAssembler::SmiUntagToFloat(FPRegister dst,
                                     Register src,
                                     UntagMode mode) {
  DCHECK(dst.Is32Bits() && src.Is64Bits());
  if (FLAG_enable_slow_asserts && (mode == kNotSpeculativeUntag)) {
    AssertSmi(src);
  }
  Scvtf(dst, src, kSmiShift);
}


void MacroAssembler::SmiTagAndPush(Register src) {
  STATIC_ASSERT((static_cast<unsigned>(kSmiShift) == kWRegSizeInBits) &&
                (static_cast<unsigned>(kSmiValueSize) == kWRegSizeInBits) &&
                (kSmiTag == 0));
  Push(src.W(), wzr);
}


void MacroAssembler::SmiTagAndPush(Register src1, Register src2) {
  STATIC_ASSERT((static_cast<unsigned>(kSmiShift) == kWRegSizeInBits) &&
                (static_cast<unsigned>(kSmiValueSize) == kWRegSizeInBits) &&
                (kSmiTag == 0));
  Push(src1.W(), wzr, src2.W(), wzr);
}


void MacroAssembler::JumpIfSmi(Register value,
                               Label* smi_label,
                               Label* not_smi_label) {
  STATIC_ASSERT((kSmiTagSize == 1) && (kSmiTag == 0));
  // Check if the tag bit is set.
  if (smi_label) {
    Tbz(value, 0, smi_label);
    if (not_smi_label) {
      B(not_smi_label);
    }
  } else {
    DCHECK(not_smi_label);
    Tbnz(value, 0, not_smi_label);
  }
}


void MacroAssembler::JumpIfNotSmi(Register value, Label* not_smi_label) {
  JumpIfSmi(value, NULL, not_smi_label);
}


void MacroAssembler::JumpIfBothSmi(Register value1,
                                   Register value2,
                                   Label* both_smi_label,
                                   Label* not_smi_label) {
  STATIC_ASSERT((kSmiTagSize == 1) && (kSmiTag == 0));
  UseScratchRegisterScope temps(this);
  Register tmp = temps.AcquireX();
  // Check if both tag bits are clear.
  Orr(tmp, value1, value2);
  JumpIfSmi(tmp, both_smi_label, not_smi_label);
}


void MacroAssembler::JumpIfEitherSmi(Register value1,
                                     Register value2,
                                     Label* either_smi_label,
                                     Label* not_smi_label) {
  STATIC_ASSERT((kSmiTagSize == 1) && (kSmiTag == 0));
  UseScratchRegisterScope temps(this);
  Register tmp = temps.AcquireX();
  // Check if either tag bit is clear.
  And(tmp, value1, value2);
  JumpIfSmi(tmp, either_smi_label, not_smi_label);
}


void MacroAssembler::JumpIfEitherNotSmi(Register value1,
                                        Register value2,
                                        Label* not_smi_label) {
  JumpIfBothSmi(value1, value2, NULL, not_smi_label);
}


void MacroAssembler::JumpIfBothNotSmi(Register value1,
                                      Register value2,
                                      Label* not_smi_label) {
  JumpIfEitherSmi(value1, value2, NULL, not_smi_label);
}


void MacroAssembler::ObjectTag(Register tagged_obj, Register obj) {
  STATIC_ASSERT(kHeapObjectTag == 1);
  if (emit_debug_code()) {
    Label ok;
    Tbz(obj, 0, &ok);
    Abort(kObjectTagged);
    Bind(&ok);
  }
  Orr(tagged_obj, obj, kHeapObjectTag);
}


void MacroAssembler::ObjectUntag(Register untagged_obj, Register obj) {
  STATIC_ASSERT(kHeapObjectTag == 1);
  if (emit_debug_code()) {
    Label ok;
    Tbnz(obj, 0, &ok);
    Abort(kObjectNotTagged);
    Bind(&ok);
  }
  Bic(untagged_obj, obj, kHeapObjectTag);
}


void MacroAssembler::IsObjectNameType(Register object,
                                      Register type,
                                      Label* fail) {
  CompareObjectType(object, type, type, LAST_NAME_TYPE);
  B(hi, fail);
}


void MacroAssembler::IsObjectJSStringType(Register object,
                                          Register type,
                                          Label* not_string,
                                          Label* string) {
  Ldr(type, FieldMemOperand(object, HeapObject::kMapOffset));
  Ldrb(type.W(), FieldMemOperand(type, Map::kInstanceTypeOffset));

  STATIC_ASSERT(kStringTag == 0);
  DCHECK((string != NULL) || (not_string != NULL));
  if (string == NULL) {
    TestAndBranchIfAnySet(type.W(), kIsNotStringMask, not_string);
  } else if (not_string == NULL) {
    TestAndBranchIfAllClear(type.W(), kIsNotStringMask, string);
  } else {
    TestAndBranchIfAnySet(type.W(), kIsNotStringMask, not_string);
    B(string);
  }
}


void MacroAssembler::Push(Handle<Object> handle) {
  UseScratchRegisterScope temps(this);
  Register tmp = temps.AcquireX();
  Mov(tmp, Operand(handle));
  Push(tmp);
}


void MacroAssembler::Claim(int64_t count, uint64_t unit_size) {
  DCHECK(count >= 0);
  uint64_t size = count * unit_size;

  if (size == 0) {
    return;
  }

  if (csp.Is(StackPointer())) {
    DCHECK(size % 16 == 0);
  } else {
    BumpSystemStackPointer(size);
  }

  Sub(StackPointer(), StackPointer(), size);
}


void MacroAssembler::Claim(const Register& count, uint64_t unit_size) {
  if (unit_size == 0) return;
  DCHECK(base::bits::IsPowerOfTwo64(unit_size));

  const int shift = CountTrailingZeros(unit_size, kXRegSizeInBits);
  const Operand size(count, LSL, shift);

  if (size.IsZero()) {
    return;
  }

  AssertPositiveOrZero(count);
  if (!csp.Is(StackPointer())) {
    BumpSystemStackPointer(size);
  }

  Sub(StackPointer(), StackPointer(), size);
}


void MacroAssembler::ClaimBySMI(const Register& count_smi, uint64_t unit_size) {
  DCHECK(unit_size == 0 || base::bits::IsPowerOfTwo64(unit_size));
  const int shift = CountTrailingZeros(unit_size, kXRegSizeInBits) - kSmiShift;
  const Operand size(count_smi,
                     (shift >= 0) ? (LSL) : (LSR),
                     (shift >= 0) ? (shift) : (-shift));

  if (size.IsZero()) {
    return;
  }

  if (!csp.Is(StackPointer())) {
    BumpSystemStackPointer(size);
  }

  Sub(StackPointer(), StackPointer(), size);
}


void MacroAssembler::Drop(int64_t count, uint64_t unit_size) {
  DCHECK(count >= 0);
  uint64_t size = count * unit_size;

  if (size == 0) {
    return;
  }

  Add(StackPointer(), StackPointer(), size);

  if (csp.Is(StackPointer())) {
    DCHECK(size % 16 == 0);
  } else if (emit_debug_code()) {
    // It is safe to leave csp where it is when unwinding the JavaScript stack,
    // but if we keep it matching StackPointer, the simulator can detect memory
    // accesses in the now-free part of the stack.
    SyncSystemStackPointer();
  }
}


void MacroAssembler::Drop(const Register& count, uint64_t unit_size) {
  if (unit_size == 0) return;
  DCHECK(base::bits::IsPowerOfTwo64(unit_size));

  const int shift = CountTrailingZeros(unit_size, kXRegSizeInBits);
  const Operand size(count, LSL, shift);

  if (size.IsZero()) {
    return;
  }

  AssertPositiveOrZero(count);
  Add(StackPointer(), StackPointer(), size);

  if (!csp.Is(StackPointer()) && emit_debug_code()) {
    // It is safe to leave csp where it is when unwinding the JavaScript stack,
    // but if we keep it matching StackPointer, the simulator can detect memory
    // accesses in the now-free part of the stack.
    SyncSystemStackPointer();
  }
}


void MacroAssembler::DropBySMI(const Register& count_smi, uint64_t unit_size) {
  DCHECK(unit_size == 0 || base::bits::IsPowerOfTwo64(unit_size));
  const int shift = CountTrailingZeros(unit_size, kXRegSizeInBits) - kSmiShift;
  const Operand size(count_smi,
                     (shift >= 0) ? (LSL) : (LSR),
                     (shift >= 0) ? (shift) : (-shift));

  if (size.IsZero()) {
    return;
  }

  Add(StackPointer(), StackPointer(), size);

  if (!csp.Is(StackPointer()) && emit_debug_code()) {
    // It is safe to leave csp where it is when unwinding the JavaScript stack,
    // but if we keep it matching StackPointer, the simulator can detect memory
    // accesses in the now-free part of the stack.
    SyncSystemStackPointer();
  }
}


void MacroAssembler::CompareAndBranch(const Register& lhs,
                                      const Operand& rhs,
                                      Condition cond,
                                      Label* label) {
  if (rhs.IsImmediate() && (rhs.ImmediateValue() == 0) &&
      ((cond == eq) || (cond == ne))) {
    if (cond == eq) {
      Cbz(lhs, label);
    } else {
      Cbnz(lhs, label);
    }
  } else {
    Cmp(lhs, rhs);
    B(cond, label);
  }
}


void MacroAssembler::TestAndBranchIfAnySet(const Register& reg,
                                           const uint64_t bit_pattern,
                                           Label* label) {
  int bits = reg.SizeInBits();
  DCHECK(CountSetBits(bit_pattern, bits) > 0);
  if (CountSetBits(bit_pattern, bits) == 1) {
    Tbnz(reg, MaskToBit(bit_pattern), label);
  } else {
    Tst(reg, bit_pattern);
    B(ne, label);
  }
}


void MacroAssembler::TestAndBranchIfAllClear(const Register& reg,
                                             const uint64_t bit_pattern,
                                             Label* label) {
  int bits = reg.SizeInBits();
  DCHECK(CountSetBits(bit_pattern, bits) > 0);
  if (CountSetBits(bit_pattern, bits) == 1) {
    Tbz(reg, MaskToBit(bit_pattern), label);
  } else {
    Tst(reg, bit_pattern);
    B(eq, label);
  }
}


void MacroAssembler::InlineData(uint64_t data) {
  DCHECK(is_uint16(data));
  InstructionAccurateScope scope(this, 1);
  movz(xzr, data);
}


void MacroAssembler::EnableInstrumentation() {
  InstructionAccurateScope scope(this, 1);
  movn(xzr, InstrumentStateEnable);
}


void MacroAssembler::DisableInstrumentation() {
  InstructionAccurateScope scope(this, 1);
  movn(xzr, InstrumentStateDisable);
}


void MacroAssembler::AnnotateInstrumentation(const char* marker_name) {
  DCHECK(strlen(marker_name) == 2);

  // We allow only printable characters in the marker names. Unprintable
  // characters are reserved for controlling features of the instrumentation.
  DCHECK(isprint(marker_name[0]) && isprint(marker_name[1]));

  InstructionAccurateScope scope(this, 1);
  movn(xzr, (marker_name[1] << 8) | marker_name[0]);
}

}  // namespace internal
}  // namespace v8

#endif  // V8_ARM64_MACRO_ASSEMBLER_ARM64_INL_H_