// Copyright 2017, 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.

#include "aarch32/macro-assembler-aarch32.h"

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)

#define CONTEXT_SCOPE \
  ContextScope context(this, __FILE__ ":" TOSTRING(__LINE__))

namespace vixl {
namespace aarch32 {

ExactAssemblyScopeWithoutPoolsCheck::ExactAssemblyScopeWithoutPoolsCheck(
    MacroAssembler* masm, size_t size, SizePolicy size_policy)
    : ExactAssemblyScope(masm,
                         size,
                         size_policy,
                         ExactAssemblyScope::kIgnorePools) {}

void UseScratchRegisterScope::Open(MacroAssembler* masm) {
  VIXL_ASSERT(masm_ == NULL);
  VIXL_ASSERT(masm != NULL);
  masm_ = masm;

  old_available_ = masm_->GetScratchRegisterList()->GetList();
  old_available_vfp_ = masm_->GetScratchVRegisterList()->GetList();

  parent_ = masm->GetCurrentScratchRegisterScope();
  masm->SetCurrentScratchRegisterScope(this);
}


void UseScratchRegisterScope::Close() {
  if (masm_ != NULL) {
    // Ensure that scopes nest perfectly, and do not outlive their parents.
    // This is a run-time check because the order of destruction of objects in
    // the _same_ scope is implementation-defined, and is likely to change in
    // optimised builds.
    VIXL_CHECK(masm_->GetCurrentScratchRegisterScope() == this);
    masm_->SetCurrentScratchRegisterScope(parent_);

    masm_->GetScratchRegisterList()->SetList(old_available_);
    masm_->GetScratchVRegisterList()->SetList(old_available_vfp_);

    masm_ = NULL;
  }
}


bool UseScratchRegisterScope::IsAvailable(const Register& reg) const {
  VIXL_ASSERT(masm_ != NULL);
  VIXL_ASSERT(reg.IsValid());
  return masm_->GetScratchRegisterList()->Includes(reg);
}


bool UseScratchRegisterScope::IsAvailable(const VRegister& reg) const {
  VIXL_ASSERT(masm_ != NULL);
  VIXL_ASSERT(reg.IsValid());
  return masm_->GetScratchVRegisterList()->IncludesAllOf(reg);
}


Register UseScratchRegisterScope::Acquire() {
  VIXL_ASSERT(masm_ != NULL);
  Register reg = masm_->GetScratchRegisterList()->GetFirstAvailableRegister();
  VIXL_CHECK(reg.IsValid());
  masm_->GetScratchRegisterList()->Remove(reg);
  return reg;
}


VRegister UseScratchRegisterScope::AcquireV(unsigned size_in_bits) {
  switch (size_in_bits) {
    case kSRegSizeInBits:
      return AcquireS();
    case kDRegSizeInBits:
      return AcquireD();
    case kQRegSizeInBits:
      return AcquireQ();
    default:
      VIXL_UNREACHABLE();
      return NoVReg;
  }
}


QRegister UseScratchRegisterScope::AcquireQ() {
  VIXL_ASSERT(masm_ != NULL);
  QRegister reg =
      masm_->GetScratchVRegisterList()->GetFirstAvailableQRegister();
  VIXL_CHECK(reg.IsValid());
  masm_->GetScratchVRegisterList()->Remove(reg);
  return reg;
}


DRegister UseScratchRegisterScope::AcquireD() {
  VIXL_ASSERT(masm_ != NULL);
  DRegister reg =
      masm_->GetScratchVRegisterList()->GetFirstAvailableDRegister();
  VIXL_CHECK(reg.IsValid());
  masm_->GetScratchVRegisterList()->Remove(reg);
  return reg;
}


SRegister UseScratchRegisterScope::AcquireS() {
  VIXL_ASSERT(masm_ != NULL);
  SRegister reg =
      masm_->GetScratchVRegisterList()->GetFirstAvailableSRegister();
  VIXL_CHECK(reg.IsValid());
  masm_->GetScratchVRegisterList()->Remove(reg);
  return reg;
}


void UseScratchRegisterScope::Release(const Register& reg) {
  VIXL_ASSERT(masm_ != NULL);
  VIXL_ASSERT(reg.IsValid());
  VIXL_ASSERT(!masm_->GetScratchRegisterList()->Includes(reg));
  masm_->GetScratchRegisterList()->Combine(reg);
}


void UseScratchRegisterScope::Release(const VRegister& reg) {
  VIXL_ASSERT(masm_ != NULL);
  VIXL_ASSERT(reg.IsValid());
  VIXL_ASSERT(!masm_->GetScratchVRegisterList()->IncludesAliasOf(reg));
  masm_->GetScratchVRegisterList()->Combine(reg);
}


void UseScratchRegisterScope::Include(const RegisterList& list) {
  VIXL_ASSERT(masm_ != NULL);
  RegisterList excluded_registers(sp, lr, pc);
  uint32_t mask = list.GetList() & ~excluded_registers.GetList();
  RegisterList* available = masm_->GetScratchRegisterList();
  available->SetList(available->GetList() | mask);
}


void UseScratchRegisterScope::Include(const VRegisterList& list) {
  VIXL_ASSERT(masm_ != NULL);
  VRegisterList* available = masm_->GetScratchVRegisterList();
  available->SetList(available->GetList() | list.GetList());
}


void UseScratchRegisterScope::Exclude(const RegisterList& list) {
  VIXL_ASSERT(masm_ != NULL);
  RegisterList* available = masm_->GetScratchRegisterList();
  available->SetList(available->GetList() & ~list.GetList());
}


void UseScratchRegisterScope::Exclude(const VRegisterList& list) {
  VIXL_ASSERT(masm_ != NULL);
  VRegisterList* available = masm_->GetScratchVRegisterList();
  available->SetList(available->GetList() & ~list.GetList());
}


void UseScratchRegisterScope::Exclude(const Operand& operand) {
  if (operand.IsImmediateShiftedRegister()) {
    Exclude(operand.GetBaseRegister());
  } else if (operand.IsRegisterShiftedRegister()) {
    Exclude(operand.GetBaseRegister(), operand.GetShiftRegister());
  } else {
    VIXL_ASSERT(operand.IsImmediate());
  }
}


void UseScratchRegisterScope::ExcludeAll() {
  VIXL_ASSERT(masm_ != NULL);
  masm_->GetScratchRegisterList()->SetList(0);
  masm_->GetScratchVRegisterList()->SetList(0);
}


void MacroAssembler::EnsureEmitPoolsFor(size_t size_arg) {
  // We skip the check when the pools are blocked.
  if (ArePoolsBlocked()) return;

  VIXL_ASSERT(IsUint32(size_arg));
  uint32_t size = static_cast<uint32_t>(size_arg);

  if (pool_manager_.MustEmit(GetCursorOffset(), size)) {
    int32_t new_pc = pool_manager_.Emit(this, GetCursorOffset(), size);
    VIXL_ASSERT(new_pc == GetCursorOffset());
    USE(new_pc);
  }
}


void MacroAssembler::HandleOutOfBoundsImmediate(Condition cond,
                                                Register tmp,
                                                uint32_t imm) {
  if (IsUintN(16, imm)) {
    CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
    mov(cond, tmp, imm & 0xffff);
    return;
  }
  if (IsUsingT32()) {
    if (ImmediateT32::IsImmediateT32(~imm)) {
      CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
      mvn(cond, tmp, ~imm);
      return;
    }
  } else {
    if (ImmediateA32::IsImmediateA32(~imm)) {
      CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
      mvn(cond, tmp, ~imm);
      return;
    }
  }
  CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
  mov(cond, tmp, imm & 0xffff);
  movt(cond, tmp, imm >> 16);
}


MemOperand MacroAssembler::MemOperandComputationHelper(
    Condition cond,
    Register scratch,
    Register base,
    uint32_t offset,
    uint32_t extra_offset_mask) {
  VIXL_ASSERT(!AliasesAvailableScratchRegister(scratch));
  VIXL_ASSERT(!AliasesAvailableScratchRegister(base));
  VIXL_ASSERT(allow_macro_instructions_);
  VIXL_ASSERT(OutsideITBlock());

  // Check for the simple pass-through case.
  if ((offset & extra_offset_mask) == offset) return MemOperand(base, offset);

  MacroEmissionCheckScope guard(this);
  ITScope it_scope(this, &cond, guard);

  uint32_t load_store_offset = offset & extra_offset_mask;
  uint32_t add_offset = offset & ~extra_offset_mask;
  if ((add_offset != 0) && (IsModifiedImmediate(offset) ||
                            IsModifiedImmediate(UnsignedNegate(offset)))) {
    load_store_offset = 0;
    add_offset = offset;
  }

  if (base.IsPC()) {
    // Special handling for PC bases. We must read the PC in the first
    // instruction (and only in that instruction), and we must also take care to
    // keep the same address calculation as loads and stores. For T32, that
    // means using something like ADR, which uses AlignDown(PC, 4).

    // We don't handle positive offsets from PC because the intention is not
    // clear; does the user expect the offset from the current
    // GetCursorOffset(), or to allow a certain amount of space after the
    // instruction?
    VIXL_ASSERT((offset & 0x80000000) != 0);
    if (IsUsingT32()) {
      // T32: make the first instruction "SUB (immediate, from PC)" -- an alias
      // of ADR -- to get behaviour like loads and stores. This ADR can handle
      // at least as much offset as the load_store_offset so it can replace it.

      uint32_t sub_pc_offset = UnsignedNegate(offset) & 0xfff;
      load_store_offset = (offset + sub_pc_offset) & extra_offset_mask;
      add_offset = (offset + sub_pc_offset) & ~extra_offset_mask;

      ExactAssemblyScope scope(this, k32BitT32InstructionSizeInBytes);
      sub(cond, scratch, base, sub_pc_offset);

      if (add_offset == 0) return MemOperand(scratch, load_store_offset);

      // The rest of the offset can be generated in the usual way.
      base = scratch;
    }
    // A32 can use any SUB instruction, so we don't have to do anything special
    // here except to ensure that we read the PC first.
  }

  add(cond, scratch, base, add_offset);
  return MemOperand(scratch, load_store_offset);
}


uint32_t MacroAssembler::GetOffsetMask(InstructionType type,
                                       AddrMode addrmode) {
  switch (type) {
    case kLdr:
    case kLdrb:
    case kStr:
    case kStrb:
      if (IsUsingA32() || (addrmode == Offset)) {
        return 0xfff;
      } else {
        return 0xff;
      }
    case kLdrsb:
    case kLdrh:
    case kLdrsh:
    case kStrh:
      if (IsUsingT32() && (addrmode == Offset)) {
        return 0xfff;
      } else {
        return 0xff;
      }
    case kVldr:
    case kVstr:
      return 0x3fc;
    case kLdrd:
    case kStrd:
      if (IsUsingA32()) {
        return 0xff;
      } else {
        return 0x3fc;
      }
    default:
      VIXL_UNREACHABLE();
      return 0;
  }
}


HARDFLOAT void PrintfTrampolineRRRR(
    const char* format, uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineRRRD(
    const char* format, uint32_t a, uint32_t b, uint32_t c, double d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineRRDR(
    const char* format, uint32_t a, uint32_t b, double c, uint32_t d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineRRDD(
    const char* format, uint32_t a, uint32_t b, double c, double d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineRDRR(
    const char* format, uint32_t a, double b, uint32_t c, uint32_t d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineRDRD(
    const char* format, uint32_t a, double b, uint32_t c, double d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineRDDR(
    const char* format, uint32_t a, double b, double c, uint32_t d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineRDDD(
    const char* format, uint32_t a, double b, double c, double d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineDRRR(
    const char* format, double a, uint32_t b, uint32_t c, uint32_t d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineDRRD(
    const char* format, double a, uint32_t b, uint32_t c, double d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineDRDR(
    const char* format, double a, uint32_t b, double c, uint32_t d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineDRDD(
    const char* format, double a, uint32_t b, double c, double d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineDDRR(
    const char* format, double a, double b, uint32_t c, uint32_t d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineDDRD(
    const char* format, double a, double b, uint32_t c, double d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineDDDR(
    const char* format, double a, double b, double c, uint32_t d) {
  printf(format, a, b, c, d);
}


HARDFLOAT void PrintfTrampolineDDDD(
    const char* format, double a, double b, double c, double d) {
  printf(format, a, b, c, d);
}


void MacroAssembler::Printf(const char* format,
                            CPURegister reg1,
                            CPURegister reg2,
                            CPURegister reg3,
                            CPURegister reg4) {
  // Exclude all registers from the available scratch registers, so
  // that we are able to use ip below.
  // TODO: Refactor this function to use UseScratchRegisterScope
  // for temporary registers below.
  UseScratchRegisterScope scratch(this);
  scratch.ExcludeAll();
  if (generate_simulator_code_) {
    PushRegister(reg4);
    PushRegister(reg3);
    PushRegister(reg2);
    PushRegister(reg1);
    Push(RegisterList(r0, r1));
    StringLiteral* format_literal =
        new StringLiteral(format, RawLiteral::kDeletedOnPlacementByPool);
    Adr(r0, format_literal);
    uint32_t args = (reg4.GetType() << 12) | (reg3.GetType() << 8) |
                    (reg2.GetType() << 4) | reg1.GetType();
    Mov(r1, args);
    Hvc(kPrintfCode);
    Pop(RegisterList(r0, r1));
    int size = reg4.GetRegSizeInBytes() + reg3.GetRegSizeInBytes() +
               reg2.GetRegSizeInBytes() + reg1.GetRegSizeInBytes();
    Drop(size);
  } else {
    // Generate on a native platform => 32 bit environment.
    // Preserve core registers r0-r3, r12, r14
    const uint32_t saved_registers_mask =
        kCallerSavedRegistersMask | (1 << r5.GetCode());
    Push(RegisterList(saved_registers_mask));
    // Push VFP registers.
    Vpush(Untyped64, DRegisterList(d0, 8));
    if (Has32DRegs()) Vpush(Untyped64, DRegisterList(d16, 16));
    // Search one register which has been saved and which doesn't need to be
    // printed.
    RegisterList available_registers(kCallerSavedRegistersMask);
    if (reg1.GetType() == CPURegister::kRRegister) {
      available_registers.Remove(Register(reg1.GetCode()));
    }
    if (reg2.GetType() == CPURegister::kRRegister) {
      available_registers.Remove(Register(reg2.GetCode()));
    }
    if (reg3.GetType() == CPURegister::kRRegister) {
      available_registers.Remove(Register(reg3.GetCode()));
    }
    if (reg4.GetType() == CPURegister::kRRegister) {
      available_registers.Remove(Register(reg4.GetCode()));
    }
    Register tmp = available_registers.GetFirstAvailableRegister();
    VIXL_ASSERT(tmp.GetType() == CPURegister::kRRegister);
    // Push the flags.
    Mrs(tmp, APSR);
    Push(tmp);
    Vmrs(RegisterOrAPSR_nzcv(tmp.GetCode()), FPSCR);
    Push(tmp);
    // Push the registers to print on the stack.
    PushRegister(reg4);
    PushRegister(reg3);
    PushRegister(reg2);
    PushRegister(reg1);
    int core_count = 1;
    int vfp_count = 0;
    uint32_t printf_type = 0;
    // Pop the registers to print and store them into r1-r3 and/or d0-d3.
    // Reg4 may stay into the stack if all the register to print are core
    // registers.
    PreparePrintfArgument(reg1, &core_count, &vfp_count, &printf_type);
    PreparePrintfArgument(reg2, &core_count, &vfp_count, &printf_type);
    PreparePrintfArgument(reg3, &core_count, &vfp_count, &printf_type);
    PreparePrintfArgument(reg4, &core_count, &vfp_count, &printf_type);
    // Ensure that the stack is aligned on 8 bytes.
    And(r5, sp, 0x7);
    if (core_count == 5) {
      // One 32 bit argument (reg4) has been left on the stack =>  align the
      // stack
      // before the argument.
      Pop(r0);
      Sub(sp, sp, r5);
      Push(r0);
    } else {
      Sub(sp, sp, r5);
    }
    // Select the right trampoline depending on the arguments.
    uintptr_t address;
    switch (printf_type) {
      case 0:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRRR);
        break;
      case 1:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineDRRR);
        break;
      case 2:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineRDRR);
        break;
      case 3:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineDDRR);
        break;
      case 4:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRDR);
        break;
      case 5:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineDRDR);
        break;
      case 6:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineRDDR);
        break;
      case 7:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineDDDR);
        break;
      case 8:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRRD);
        break;
      case 9:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineDRRD);
        break;
      case 10:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineRDRD);
        break;
      case 11:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineDDRD);
        break;
      case 12:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRDD);
        break;
      case 13:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineDRDD);
        break;
      case 14:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineRDDD);
        break;
      case 15:
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineDDDD);
        break;
      default:
        VIXL_UNREACHABLE();
        address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRRR);
        break;
    }
    StringLiteral* format_literal =
        new StringLiteral(format, RawLiteral::kDeletedOnPlacementByPool);
    Adr(r0, format_literal);
    Mov(ip, Operand::From(address));
    Blx(ip);
    // If register reg4 was left on the stack => skip it.
    if (core_count == 5) Drop(kRegSizeInBytes);
    // Restore the stack as it was before alignment.
    Add(sp, sp, r5);
    // Restore the flags.
    Pop(tmp);
    Vmsr(FPSCR, tmp);
    Pop(tmp);
    Msr(APSR_nzcvqg, tmp);
    // Restore the registers.
    if (Has32DRegs()) Vpop(Untyped64, DRegisterList(d16, 16));
    Vpop(Untyped64, DRegisterList(d0, 8));
    Pop(RegisterList(saved_registers_mask));
  }
}


void MacroAssembler::PushRegister(CPURegister reg) {
  switch (reg.GetType()) {
    case CPURegister::kNoRegister:
      break;
    case CPURegister::kRRegister:
      Push(Register(reg.GetCode()));
      break;
    case CPURegister::kSRegister:
      Vpush(Untyped32, SRegisterList(SRegister(reg.GetCode())));
      break;
    case CPURegister::kDRegister:
      Vpush(Untyped64, DRegisterList(DRegister(reg.GetCode())));
      break;
    case CPURegister::kQRegister:
      VIXL_UNIMPLEMENTED();
      break;
  }
}


void MacroAssembler::PreparePrintfArgument(CPURegister reg,
                                           int* core_count,
                                           int* vfp_count,
                                           uint32_t* printf_type) {
  switch (reg.GetType()) {
    case CPURegister::kNoRegister:
      break;
    case CPURegister::kRRegister:
      VIXL_ASSERT(*core_count <= 4);
      if (*core_count < 4) Pop(Register(*core_count));
      *core_count += 1;
      break;
    case CPURegister::kSRegister:
      VIXL_ASSERT(*vfp_count < 4);
      *printf_type |= 1 << (*core_count + *vfp_count - 1);
      Vpop(Untyped32, SRegisterList(SRegister(*vfp_count * 2)));
      Vcvt(F64, F32, DRegister(*vfp_count), SRegister(*vfp_count * 2));
      *vfp_count += 1;
      break;
    case CPURegister::kDRegister:
      VIXL_ASSERT(*vfp_count < 4);
      *printf_type |= 1 << (*core_count + *vfp_count - 1);
      Vpop(Untyped64, DRegisterList(DRegister(*vfp_count)));
      *vfp_count += 1;
      break;
    case CPURegister::kQRegister:
      VIXL_UNIMPLEMENTED();
      break;
  }
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondROp instruction,
                              Condition cond,
                              Register rn,
                              const Operand& operand) {
  VIXL_ASSERT((type == kMovt) || (type == kSxtb16) || (type == kTeq) ||
              (type == kUxtb16));

  if (type == kMovt) {
    VIXL_ABORT_WITH_MSG("`Movt` expects a 16-bit immediate.\n");
  }

  // This delegate only supports teq with immediates.
  CONTEXT_SCOPE;
  if ((type == kTeq) && operand.IsImmediate()) {
    UseScratchRegisterScope temps(this);
    Register scratch = temps.Acquire();
    HandleOutOfBoundsImmediate(cond, scratch, operand.GetImmediate());
    CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
    teq(cond, rn, scratch);
    return;
  }
  Assembler::Delegate(type, instruction, cond, rn, operand);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondSizeROp instruction,
                              Condition cond,
                              EncodingSize size,
                              Register rn,
                              const Operand& operand) {
  CONTEXT_SCOPE;
  VIXL_ASSERT(size.IsBest());
  VIXL_ASSERT((type == kCmn) || (type == kCmp) || (type == kMov) ||
              (type == kMovs) || (type == kMvn) || (type == kMvns) ||
              (type == kSxtb) || (type == kSxth) || (type == kTst) ||
              (type == kUxtb) || (type == kUxth));
  if (IsUsingT32() && operand.IsRegisterShiftedRegister()) {
    VIXL_ASSERT((type != kMov) || (type != kMovs));
    InstructionCondRROp shiftop = NULL;
    switch (operand.GetShift().GetType()) {
      case LSL:
        shiftop = &Assembler::lsl;
        break;
      case LSR:
        shiftop = &Assembler::lsr;
        break;
      case ASR:
        shiftop = &Assembler::asr;
        break;
      case RRX:
        // A RegisterShiftedRegister operand cannot have a shift of type RRX.
        VIXL_UNREACHABLE();
        break;
      case ROR:
        shiftop = &Assembler::ror;
        break;
      default:
        VIXL_UNREACHABLE();
    }
    if (shiftop != NULL) {
      UseScratchRegisterScope temps(this);
      Register scratch = temps.Acquire();
      CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
      (this->*shiftop)(cond,
                       scratch,
                       operand.GetBaseRegister(),
                       operand.GetShiftRegister());
      (this->*instruction)(cond, size, rn, scratch);
      return;
    }
  }
  if (operand.IsImmediate()) {
    uint32_t imm = operand.GetImmediate();
    switch (type) {
      case kMov:
      case kMovs:
        if (!rn.IsPC()) {
          // Immediate is too large, but not using PC, so handle with mov{t}.
          HandleOutOfBoundsImmediate(cond, rn, imm);
          if (type == kMovs) {
            CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
            tst(cond, rn, rn);
          }
          return;
        } else if (type == kMov) {
          VIXL_ASSERT(IsUsingA32() || cond.Is(al));
          // Immediate is too large and using PC, so handle using a temporary
          // register.
          UseScratchRegisterScope temps(this);
          Register scratch = temps.Acquire();
          HandleOutOfBoundsImmediate(al, scratch, imm);
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          bx(cond, scratch);
          return;
        }
        break;
      case kCmn:
      case kCmp:
        if (IsUsingA32() || !rn.IsPC()) {
          UseScratchRegisterScope temps(this);
          Register scratch = temps.Acquire();
          HandleOutOfBoundsImmediate(cond, scratch, imm);
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, size, rn, scratch);
          return;
        }
        break;
      case kMvn:
      case kMvns:
        if (!rn.IsPC()) {
          UseScratchRegisterScope temps(this);
          Register scratch = temps.Acquire();
          HandleOutOfBoundsImmediate(cond, scratch, imm);
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, size, rn, scratch);
          return;
        }
        break;
      case kTst:
        if (IsUsingA32() || !rn.IsPC()) {
          UseScratchRegisterScope temps(this);
          Register scratch = temps.Acquire();
          HandleOutOfBoundsImmediate(cond, scratch, imm);
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, size, rn, scratch);
          return;
        }
        break;
      default:  // kSxtb, Sxth, Uxtb, Uxth
        break;
    }
  }
  Assembler::Delegate(type, instruction, cond, size, rn, operand);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondRROp instruction,
                              Condition cond,
                              Register rd,
                              Register rn,
                              const Operand& operand) {
  if ((type == kSxtab) || (type == kSxtab16) || (type == kSxtah) ||
      (type == kUxtab) || (type == kUxtab16) || (type == kUxtah) ||
      (type == kPkhbt) || (type == kPkhtb)) {
    UnimplementedDelegate(type);
    return;
  }

  // This delegate only handles the following instructions.
  VIXL_ASSERT((type == kOrn) || (type == kOrns) || (type == kRsc) ||
              (type == kRscs));
  CONTEXT_SCOPE;

  // T32 does not support register shifted register operands, emulate it.
  if (IsUsingT32() && operand.IsRegisterShiftedRegister()) {
    InstructionCondRROp shiftop = NULL;
    switch (operand.GetShift().GetType()) {
      case LSL:
        shiftop = &Assembler::lsl;
        break;
      case LSR:
        shiftop = &Assembler::lsr;
        break;
      case ASR:
        shiftop = &Assembler::asr;
        break;
      case RRX:
        // A RegisterShiftedRegister operand cannot have a shift of type RRX.
        VIXL_UNREACHABLE();
        break;
      case ROR:
        shiftop = &Assembler::ror;
        break;
      default:
        VIXL_UNREACHABLE();
    }
    if (shiftop != NULL) {
      UseScratchRegisterScope temps(this);
      Register rm = operand.GetBaseRegister();
      Register rs = operand.GetShiftRegister();
      // Try to use rd as a scratch register. We can do this if it aliases rs or
      // rm (because we read them in the first instruction), but not rn.
      if (!rd.Is(rn)) temps.Include(rd);
      Register scratch = temps.Acquire();
      // TODO: The scope length was measured empirically. We should analyse the
      // worst-case size and add targetted tests.
      CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
      (this->*shiftop)(cond, scratch, rm, rs);
      (this->*instruction)(cond, rd, rn, scratch);
      return;
    }
  }

  // T32 does not have a Rsc instruction, negate the lhs input and turn it into
  // an Adc. Adc and Rsc are equivalent using a bitwise NOT:
  //   adc rd, rn, operand <-> rsc rd, NOT(rn), operand
  if (IsUsingT32() && ((type == kRsc) || (type == kRscs))) {
    // The RegisterShiftRegister case should have been handled above.
    VIXL_ASSERT(!operand.IsRegisterShiftedRegister());
    UseScratchRegisterScope temps(this);
    // Try to use rd as a scratch register. We can do this if it aliases rn
    // (because we read it in the first instruction), but not rm.
    temps.Include(rd);
    temps.Exclude(operand);
    Register negated_rn = temps.Acquire();
    {
      CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
      mvn(cond, negated_rn, rn);
    }
    if (type == kRsc) {
      CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
      adc(cond, rd, negated_rn, operand);
      return;
    }
    // TODO: We shouldn't have to specify how much space the next instruction
    // needs.
    CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
    adcs(cond, rd, negated_rn, operand);
    return;
  }

  if (operand.IsImmediate()) {
    // If the immediate can be encoded when inverted, turn Orn into Orr.
    // Otherwise rely on HandleOutOfBoundsImmediate to generate a series of
    // mov.
    int32_t imm = operand.GetSignedImmediate();
    if (((type == kOrn) || (type == kOrns)) && IsModifiedImmediate(~imm)) {
      CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
      switch (type) {
        case kOrn:
          orr(cond, rd, rn, ~imm);
          return;
        case kOrns:
          orrs(cond, rd, rn, ~imm);
          return;
        default:
          VIXL_UNREACHABLE();
          break;
      }
    }
  }

  // A32 does not have a Orn instruction, negate the rhs input and turn it into
  // a Orr.
  if (IsUsingA32() && ((type == kOrn) || (type == kOrns))) {
    // TODO: orn r0, r1, imm -> orr r0, r1, neg(imm) if doable
    //  mvn r0, r2
    //  orr r0, r1, r0
    Register scratch;
    UseScratchRegisterScope temps(this);
    // Try to use rd as a scratch register. We can do this if it aliases rs or
    // rm (because we read them in the first instruction), but not rn.
    if (!rd.Is(rn)) temps.Include(rd);
    scratch = temps.Acquire();
    {
      // TODO: We shouldn't have to specify how much space the next instruction
      // needs.
      CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
      mvn(cond, scratch, operand);
    }
    if (type == kOrns) {
      CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
      orrs(cond, rd, rn, scratch);
      return;
    }
    CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
    orr(cond, rd, rn, scratch);
    return;
  }

  if (operand.IsImmediate()) {
    UseScratchRegisterScope temps(this);
    // Allow using the destination as a scratch register if possible.
    if (!rd.Is(rn)) temps.Include(rd);
    Register scratch = temps.Acquire();
    int32_t imm = operand.GetSignedImmediate();
    HandleOutOfBoundsImmediate(cond, scratch, imm);
    CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
    (this->*instruction)(cond, rd, rn, scratch);
    return;
  }
  Assembler::Delegate(type, instruction, cond, rd, rn, operand);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondSizeRL instruction,
                              Condition cond,
                              EncodingSize size,
                              Register rd,
                              Location* location) {
  VIXL_ASSERT((type == kLdr) || (type == kAdr));

  CONTEXT_SCOPE;
  VIXL_ASSERT(size.IsBest());

  if ((type == kLdr) && location->IsBound()) {
    CodeBufferCheckScope scope(this, 5 * kMaxInstructionSizeInBytes);
    UseScratchRegisterScope temps(this);
    temps.Include(rd);
    uint32_t mask = GetOffsetMask(type, Offset);
    ldr(rd, MemOperandComputationHelper(cond, temps.Acquire(), location, mask));
    return;
  }

  Assembler::Delegate(type, instruction, cond, size, rd, location);
}


bool MacroAssembler::GenerateSplitInstruction(
    InstructionCondSizeRROp instruction,
    Condition cond,
    Register rd,
    Register rn,
    uint32_t imm,
    uint32_t mask) {
  uint32_t high = imm & ~mask;
  if (!IsModifiedImmediate(high) && !rn.IsPC()) return false;
  // If high is a modified immediate, we can perform the operation with
  // only 2 instructions.
  // Else, if rn is PC, we want to avoid moving PC into a temporary.
  // Therefore, we also use the pattern even if the second call may
  // generate 3 instructions.
  uint32_t low = imm & mask;
  CodeBufferCheckScope scope(this,
                             (rn.IsPC() ? 4 : 2) * kMaxInstructionSizeInBytes);
  (this->*instruction)(cond, Best, rd, rn, low);
  (this->*instruction)(cond, Best, rd, rd, high);
  return true;
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondSizeRROp instruction,
                              Condition cond,
                              EncodingSize size,
                              Register rd,
                              Register rn,
                              const Operand& operand) {
  VIXL_ASSERT(
      (type == kAdc) || (type == kAdcs) || (type == kAdd) || (type == kAdds) ||
      (type == kAnd) || (type == kAnds) || (type == kAsr) || (type == kAsrs) ||
      (type == kBic) || (type == kBics) || (type == kEor) || (type == kEors) ||
      (type == kLsl) || (type == kLsls) || (type == kLsr) || (type == kLsrs) ||
      (type == kOrr) || (type == kOrrs) || (type == kRor) || (type == kRors) ||
      (type == kRsb) || (type == kRsbs) || (type == kSbc) || (type == kSbcs) ||
      (type == kSub) || (type == kSubs));

  CONTEXT_SCOPE;
  VIXL_ASSERT(size.IsBest());
  if (IsUsingT32() && operand.IsRegisterShiftedRegister()) {
    InstructionCondRROp shiftop = NULL;
    switch (operand.GetShift().GetType()) {
      case LSL:
        shiftop = &Assembler::lsl;
        break;
      case LSR:
        shiftop = &Assembler::lsr;
        break;
      case ASR:
        shiftop = &Assembler::asr;
        break;
      case RRX:
        // A RegisterShiftedRegister operand cannot have a shift of type RRX.
        VIXL_UNREACHABLE();
        break;
      case ROR:
        shiftop = &Assembler::ror;
        break;
      default:
        VIXL_UNREACHABLE();
    }
    if (shiftop != NULL) {
      UseScratchRegisterScope temps(this);
      Register rm = operand.GetBaseRegister();
      Register rs = operand.GetShiftRegister();
      // Try to use rd as a scratch register. We can do this if it aliases rs or
      // rm (because we read them in the first instruction), but not rn.
      if (!rd.Is(rn)) temps.Include(rd);
      Register scratch = temps.Acquire();
      CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
      (this->*shiftop)(cond, scratch, rm, rs);
      (this->*instruction)(cond, size, rd, rn, scratch);
      return;
    }
  }
  if (operand.IsImmediate()) {
    int32_t imm = operand.GetSignedImmediate();
    if (ImmediateT32::IsImmediateT32(~imm)) {
      if (IsUsingT32()) {
        switch (type) {
          case kOrr:
            orn(cond, rd, rn, ~imm);
            return;
          case kOrrs:
            orns(cond, rd, rn, ~imm);
            return;
          default:
            break;
        }
      }
    }
    if (imm < 0) {
      InstructionCondSizeRROp asmcb = NULL;
      // Add and sub are equivalent using an arithmetic negation:
      //   add rd, rn, #imm <-> sub rd, rn, - #imm
      // Add and sub with carry are equivalent using a bitwise NOT:
      //   adc rd, rn, #imm <-> sbc rd, rn, NOT #imm
      switch (type) {
        case kAdd:
          asmcb = &Assembler::sub;
          imm = -imm;
          break;
        case kAdds:
          asmcb = &Assembler::subs;
          imm = -imm;
          break;
        case kSub:
          asmcb = &Assembler::add;
          imm = -imm;
          break;
        case kSubs:
          asmcb = &Assembler::adds;
          imm = -imm;
          break;
        case kAdc:
          asmcb = &Assembler::sbc;
          imm = ~imm;
          break;
        case kAdcs:
          asmcb = &Assembler::sbcs;
          imm = ~imm;
          break;
        case kSbc:
          asmcb = &Assembler::adc;
          imm = ~imm;
          break;
        case kSbcs:
          asmcb = &Assembler::adcs;
          imm = ~imm;
          break;
        default:
          break;
      }
      if (asmcb != NULL) {
        CodeBufferCheckScope scope(this, 4 * kMaxInstructionSizeInBytes);
        (this->*asmcb)(cond, size, rd, rn, Operand(imm));
        return;
      }
    }

    // When rn is PC, only handle negative offsets. The correct way to handle
    // positive offsets isn't clear; does the user want the offset from the
    // start of the macro, or from the end (to allow a certain amount of space)?
    // When type is Add or Sub, imm is always positive (imm < 0 has just been
    // handled and imm == 0 would have been generated without the need of a
    // delegate). Therefore, only add to PC is forbidden here.
    if ((((type == kAdd) && !rn.IsPC()) || (type == kSub)) &&
        (IsUsingA32() || (!rd.IsPC() && !rn.IsPC()))) {
      VIXL_ASSERT(imm > 0);
      // Try to break the constant into two modified immediates.
      // For T32 also try to break the constant into one imm12 and one modified
      // immediate. Count the trailing zeroes and get the biggest even value.
      int trailing_zeroes = CountTrailingZeros(imm) & ~1u;
      uint32_t mask = ((trailing_zeroes < 4) && IsUsingT32())
                          ? 0xfff
                          : (0xff << trailing_zeroes);
      if (GenerateSplitInstruction(instruction, cond, rd, rn, imm, mask)) {
        return;
      }
      InstructionCondSizeRROp asmcb = NULL;
      switch (type) {
        case kAdd:
          asmcb = &Assembler::sub;
          break;
        case kSub:
          asmcb = &Assembler::add;
          break;
        default:
          VIXL_UNREACHABLE();
      }
      if (GenerateSplitInstruction(asmcb, cond, rd, rn, -imm, mask)) {
        return;
      }
    }

    UseScratchRegisterScope temps(this);
    // Allow using the destination as a scratch register if possible.
    if (!rd.Is(rn)) temps.Include(rd);
    if (rn.IsPC()) {
      // If we're reading the PC, we need to do it in the first instruction,
      // otherwise we'll read the wrong value. We rely on this to handle the
      // long-range PC-relative MemOperands which can result from user-managed
      // literals.

      // Only handle negative offsets. The correct way to handle positive
      // offsets isn't clear; does the user want the offset from the start of
      // the macro, or from the end (to allow a certain amount of space)?
      bool offset_is_negative_or_zero = (imm <= 0);
      switch (type) {
        case kAdd:
        case kAdds:
          offset_is_negative_or_zero = (imm <= 0);
          break;
        case kSub:
        case kSubs:
          offset_is_negative_or_zero = (imm >= 0);
          break;
        case kAdc:
        case kAdcs:
          offset_is_negative_or_zero = (imm < 0);
          break;
        case kSbc:
        case kSbcs:
          offset_is_negative_or_zero = (imm > 0);
          break;
        default:
          break;
      }
      if (offset_is_negative_or_zero) {
        {
          rn = temps.Acquire();
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          mov(cond, rn, pc);
        }
        // Recurse rather than falling through, to try to get the immediate into
        // a single instruction.
        CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
        (this->*instruction)(cond, size, rd, rn, operand);
        return;
      }
    } else {
      Register scratch = temps.Acquire();
      // TODO: The scope length was measured empirically. We should analyse the
      // worst-case size and add targetted tests.
      CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
      mov(cond, scratch, operand.GetImmediate());
      (this->*instruction)(cond, size, rd, rn, scratch);
      return;
    }
  }
  Assembler::Delegate(type, instruction, cond, size, rd, rn, operand);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionRL instruction,
                              Register rn,
                              Location* location) {
  VIXL_ASSERT((type == kCbz) || (type == kCbnz));

  CONTEXT_SCOPE;
  CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
  if (IsUsingA32()) {
    if (type == kCbz) {
      VIXL_ABORT_WITH_MSG("Cbz is only available for T32.\n");
    } else {
      VIXL_ABORT_WITH_MSG("Cbnz is only available for T32.\n");
    }
  } else if (rn.IsLow()) {
    switch (type) {
      case kCbnz: {
        Label done;
        cbz(rn, &done);
        b(location);
        Bind(&done);
        return;
      }
      case kCbz: {
        Label done;
        cbnz(rn, &done);
        b(location);
        Bind(&done);
        return;
      }
      default:
        break;
    }
  }
  Assembler::Delegate(type, instruction, rn, location);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondSizeL instruction,
                              Condition cond,
                              EncodingSize size,
                              Location* location) {
  VIXL_ASSERT(type == kB);

  CONTEXT_SCOPE;

  // Apply veneer to increase range of backwards conditional branches.
  // This replaces:
  //   label:
  //    <instructions>
  //    bcond label   ; T3
  // With:
  //   label:
  //    <instructions>
  //    binvcond skip ; T1
  //    b label       ; T4
  //   skip:
  Location::Offset offset = location->GetLocation() -
    (GetCursorOffset() + GetArchitectureStatePCOffset());
  if (IsUsingT32() && location->IsBound() && ((offset & 0x1) == 0) &&
      !cond.Is(al) && cond.IsNotNever()) {
    // Bound locations must be earlier in the code.
    VIXL_ASSERT(offset < 0);

    // The offset must be within range of a T4 branch, accounting for the
    // conditional branch (T1) we emit first, in order to jump over it.
    offset -= k16BitT32InstructionSizeInBytes;
    if (offset >= -16777216) {
      CodeBufferCheckScope scope(this, k16BitT32InstructionSizeInBytes +
                                       k32BitT32InstructionSizeInBytes);
      Label skip;
      b(cond.Negate(), Narrow, &skip);
      b(location);
      Bind(&skip);
      return;
    } else {
      VIXL_ABORT_WITH_MSG("Conditional branch too far for veneer.\n");
    }
  }

  Assembler::Delegate(type, instruction, cond, size, location);
}


template <typename T>
static inline bool IsI64BitPattern(T imm) {
  for (T mask = 0xff << ((sizeof(T) - 1) * 8); mask != 0; mask >>= 8) {
    if (((imm & mask) != mask) && ((imm & mask) != 0)) return false;
  }
  return true;
}


template <typename T>
static inline bool IsI8BitPattern(T imm) {
  uint8_t imm8 = imm & 0xff;
  for (unsigned rep = sizeof(T) - 1; rep > 0; rep--) {
    imm >>= 8;
    if ((imm & 0xff) != imm8) return false;
  }
  return true;
}


static inline bool CanBeInverted(uint32_t imm32) {
  uint32_t fill8 = 0;

  if ((imm32 & 0xffffff00) == 0xffffff00) {
    //    11111111 11111111 11111111 abcdefgh
    return true;
  }
  if (((imm32 & 0xff) == 0) || ((imm32 & 0xff) == 0xff)) {
    fill8 = imm32 & 0xff;
    imm32 >>= 8;
    if ((imm32 >> 8) == 0xffff) {
      //    11111111 11111111 abcdefgh 00000000
      // or 11111111 11111111 abcdefgh 11111111
      return true;
    }
    if ((imm32 & 0xff) == fill8) {
      imm32 >>= 8;
      if ((imm32 >> 8) == 0xff) {
        //    11111111 abcdefgh 00000000 00000000
        // or 11111111 abcdefgh 11111111 11111111
        return true;
      }
      if ((fill8 == 0xff) && ((imm32 & 0xff) == 0xff)) {
        //    abcdefgh 11111111 11111111 11111111
        return true;
      }
    }
  }
  return false;
}


template <typename RES, typename T>
static inline RES replicate(T imm) {
  VIXL_ASSERT((sizeof(RES) > sizeof(T)) &&
              (((sizeof(RES) / sizeof(T)) * sizeof(T)) == sizeof(RES)));
  RES res = imm;
  for (unsigned i = sizeof(RES) / sizeof(T) - 1; i > 0; i--) {
    res = (res << (sizeof(T) * 8)) | imm;
  }
  return res;
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondDtSSop instruction,
                              Condition cond,
                              DataType dt,
                              SRegister rd,
                              const SOperand& operand) {
  CONTEXT_SCOPE;
  if (type == kVmov) {
    if (operand.IsImmediate() && dt.Is(F32)) {
      const NeonImmediate& neon_imm = operand.GetNeonImmediate();
      if (neon_imm.CanConvert<float>()) {
        // movw ip, imm16
        // movk ip, imm16
        // vmov s0, ip
        UseScratchRegisterScope temps(this);
        Register scratch = temps.Acquire();
        float f = neon_imm.GetImmediate<float>();
        // TODO: The scope length was measured empirically. We should analyse
        // the
        // worst-case size and add targetted tests.
        CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
        mov(cond, scratch, FloatToRawbits(f));
        vmov(cond, rd, scratch);
        return;
      }
    }
  }
  Assembler::Delegate(type, instruction, cond, dt, rd, operand);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondDtDDop instruction,
                              Condition cond,
                              DataType dt,
                              DRegister rd,
                              const DOperand& operand) {
  CONTEXT_SCOPE;
  if (type == kVmov) {
    if (operand.IsImmediate()) {
      const NeonImmediate& neon_imm = operand.GetNeonImmediate();
      switch (dt.GetValue()) {
        case I32:
          if (neon_imm.CanConvert<uint32_t>()) {
            uint32_t imm = neon_imm.GetImmediate<uint32_t>();
            // vmov.i32 d0, 0xabababab will translate into vmov.i8 d0, 0xab
            if (IsI8BitPattern(imm)) {
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vmov(cond, I8, rd, imm & 0xff);
              return;
            }
            // vmov.i32 d0, 0xff0000ff will translate into
            // vmov.i64 d0, 0xff0000ffff0000ff
            if (IsI64BitPattern(imm)) {
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vmov(cond, I64, rd, replicate<uint64_t>(imm));
              return;
            }
            // vmov.i32 d0, 0xffab0000 will translate into
            // vmvn.i32 d0, 0x0054ffff
            if (cond.Is(al) && CanBeInverted(imm)) {
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vmvn(I32, rd, ~imm);
              return;
            }
          }
          break;
        case I16:
          if (neon_imm.CanConvert<uint16_t>()) {
            uint16_t imm = neon_imm.GetImmediate<uint16_t>();
            // vmov.i16 d0, 0xabab will translate into vmov.i8 d0, 0xab
            if (IsI8BitPattern(imm)) {
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vmov(cond, I8, rd, imm & 0xff);
              return;
            }
          }
          break;
        case I64:
          if (neon_imm.CanConvert<uint64_t>()) {
            uint64_t imm = neon_imm.GetImmediate<uint64_t>();
            // vmov.i64 d0, -1 will translate into vmov.i8 d0, 0xff
            if (IsI8BitPattern(imm)) {
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vmov(cond, I8, rd, imm & 0xff);
              return;
            }
            // mov ip, lo(imm64)
            // vdup d0, ip
            // vdup is prefered to 'vmov d0[0]' as d0[1] does not need to be
            // preserved
            {
              UseScratchRegisterScope temps(this);
              Register scratch = temps.Acquire();
              {
                // TODO: The scope length was measured empirically. We should
                // analyse the
                // worst-case size and add targetted tests.
                CodeBufferCheckScope scope(this,
                                           2 * kMaxInstructionSizeInBytes);
                mov(cond, scratch, static_cast<uint32_t>(imm & 0xffffffff));
              }
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vdup(cond, Untyped32, rd, scratch);
            }
            // mov ip, hi(imm64)
            // vmov d0[1], ip
            {
              UseScratchRegisterScope temps(this);
              Register scratch = temps.Acquire();
              {
                // TODO: The scope length was measured empirically. We should
                // analyse the
                // worst-case size and add targetted tests.
                CodeBufferCheckScope scope(this,
                                           2 * kMaxInstructionSizeInBytes);
                mov(cond, scratch, static_cast<uint32_t>(imm >> 32));
              }
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vmov(cond, Untyped32, DRegisterLane(rd, 1), scratch);
            }
            return;
          }
          break;
        default:
          break;
      }
      VIXL_ASSERT(!dt.Is(I8));  // I8 cases should have been handled already.
      if ((dt.Is(I16) || dt.Is(I32)) && neon_imm.CanConvert<uint32_t>()) {
        // mov ip, imm32
        // vdup.16 d0, ip
        UseScratchRegisterScope temps(this);
        Register scratch = temps.Acquire();
        {
          CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
          mov(cond, scratch, neon_imm.GetImmediate<uint32_t>());
        }
        DataTypeValue vdup_dt = Untyped32;
        switch (dt.GetValue()) {
          case I16:
            vdup_dt = Untyped16;
            break;
          case I32:
            vdup_dt = Untyped32;
            break;
          default:
            VIXL_UNREACHABLE();
        }
        CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
        vdup(cond, vdup_dt, rd, scratch);
        return;
      }
      if (dt.Is(F32) && neon_imm.CanConvert<float>()) {
        float f = neon_imm.GetImmediate<float>();
        // Punt to vmov.i32
        // TODO: The scope length was guessed based on the double case below. We
        // should analyse the worst-case size and add targetted tests.
        CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
        vmov(cond, I32, rd, FloatToRawbits(f));
        return;
      }
      if (dt.Is(F64) && neon_imm.CanConvert<double>()) {
        // Punt to vmov.i64
        double d = neon_imm.GetImmediate<double>();
        // TODO: The scope length was measured empirically. We should analyse
        // the
        // worst-case size and add targetted tests.
        CodeBufferCheckScope scope(this, 6 * kMaxInstructionSizeInBytes);
        vmov(cond, I64, rd, DoubleToRawbits(d));
        return;
      }
    }
  }
  Assembler::Delegate(type, instruction, cond, dt, rd, operand);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondDtQQop instruction,
                              Condition cond,
                              DataType dt,
                              QRegister rd,
                              const QOperand& operand) {
  CONTEXT_SCOPE;
  if (type == kVmov) {
    if (operand.IsImmediate()) {
      const NeonImmediate& neon_imm = operand.GetNeonImmediate();
      switch (dt.GetValue()) {
        case I32:
          if (neon_imm.CanConvert<uint32_t>()) {
            uint32_t imm = neon_imm.GetImmediate<uint32_t>();
            // vmov.i32 d0, 0xabababab will translate into vmov.i8 d0, 0xab
            if (IsI8BitPattern(imm)) {
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vmov(cond, I8, rd, imm & 0xff);
              return;
            }
            // vmov.i32 d0, 0xff0000ff will translate into
            // vmov.i64 d0, 0xff0000ffff0000ff
            if (IsI64BitPattern(imm)) {
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vmov(cond, I64, rd, replicate<uint64_t>(imm));
              return;
            }
            // vmov.i32 d0, 0xffab0000 will translate into
            // vmvn.i32 d0, 0x0054ffff
            if (CanBeInverted(imm)) {
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vmvn(cond, I32, rd, ~imm);
              return;
            }
          }
          break;
        case I16:
          if (neon_imm.CanConvert<uint16_t>()) {
            uint16_t imm = neon_imm.GetImmediate<uint16_t>();
            // vmov.i16 d0, 0xabab will translate into vmov.i8 d0, 0xab
            if (IsI8BitPattern(imm)) {
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vmov(cond, I8, rd, imm & 0xff);
              return;
            }
          }
          break;
        case I64:
          if (neon_imm.CanConvert<uint64_t>()) {
            uint64_t imm = neon_imm.GetImmediate<uint64_t>();
            // vmov.i64 d0, -1 will translate into vmov.i8 d0, 0xff
            if (IsI8BitPattern(imm)) {
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vmov(cond, I8, rd, imm & 0xff);
              return;
            }
            // mov ip, lo(imm64)
            // vdup q0, ip
            // vdup is prefered to 'vmov d0[0]' as d0[1-3] don't need to be
            // preserved
            {
              UseScratchRegisterScope temps(this);
              Register scratch = temps.Acquire();
              {
                CodeBufferCheckScope scope(this,
                                           2 * kMaxInstructionSizeInBytes);
                mov(cond, scratch, static_cast<uint32_t>(imm & 0xffffffff));
              }
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vdup(cond, Untyped32, rd, scratch);
            }
            // mov ip, hi(imm64)
            // vmov.i32 d0[1], ip
            // vmov d1, d0
            {
              UseScratchRegisterScope temps(this);
              Register scratch = temps.Acquire();
              {
                CodeBufferCheckScope scope(this,
                                           2 * kMaxInstructionSizeInBytes);
                mov(cond, scratch, static_cast<uint32_t>(imm >> 32));
              }
              {
                CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
                vmov(cond,
                     Untyped32,
                     DRegisterLane(rd.GetLowDRegister(), 1),
                     scratch);
              }
              CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
              vmov(cond, F64, rd.GetHighDRegister(), rd.GetLowDRegister());
            }
            return;
          }
          break;
        default:
          break;
      }
      VIXL_ASSERT(!dt.Is(I8));  // I8 cases should have been handled already.
      if ((dt.Is(I16) || dt.Is(I32)) && neon_imm.CanConvert<uint32_t>()) {
        // mov ip, imm32
        // vdup.16 d0, ip
        UseScratchRegisterScope temps(this);
        Register scratch = temps.Acquire();
        {
          CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
          mov(cond, scratch, neon_imm.GetImmediate<uint32_t>());
        }
        DataTypeValue vdup_dt = Untyped32;
        switch (dt.GetValue()) {
          case I16:
            vdup_dt = Untyped16;
            break;
          case I32:
            vdup_dt = Untyped32;
            break;
          default:
            VIXL_UNREACHABLE();
        }
        CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
        vdup(cond, vdup_dt, rd, scratch);
        return;
      }
      if (dt.Is(F32) && neon_imm.CanConvert<float>()) {
        // Punt to vmov.i64
        float f = neon_imm.GetImmediate<float>();
        CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
        vmov(cond, I32, rd, FloatToRawbits(f));
        return;
      }
      if (dt.Is(F64) && neon_imm.CanConvert<double>()) {
        // Use vmov to create the double in the low D register, then duplicate
        // it into the high D register.
        double d = neon_imm.GetImmediate<double>();
        CodeBufferCheckScope scope(this, 7 * kMaxInstructionSizeInBytes);
        vmov(cond, F64, rd.GetLowDRegister(), d);
        vmov(cond, F64, rd.GetHighDRegister(), rd.GetLowDRegister());
        return;
      }
    }
  }
  Assembler::Delegate(type, instruction, cond, dt, rd, operand);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondRL instruction,
                              Condition cond,
                              Register rt,
                              Location* location) {
  VIXL_ASSERT((type == kLdrb) || (type == kLdrh) || (type == kLdrsb) ||
              (type == kLdrsh));

  CONTEXT_SCOPE;

  if (location->IsBound()) {
    CodeBufferCheckScope scope(this, 5 * kMaxInstructionSizeInBytes);
    UseScratchRegisterScope temps(this);
    temps.Include(rt);
    Register scratch = temps.Acquire();
    uint32_t mask = GetOffsetMask(type, Offset);
    switch (type) {
      case kLdrb:
        ldrb(rt, MemOperandComputationHelper(cond, scratch, location, mask));
        return;
      case kLdrh:
        ldrh(rt, MemOperandComputationHelper(cond, scratch, location, mask));
        return;
      case kLdrsb:
        ldrsb(rt, MemOperandComputationHelper(cond, scratch, location, mask));
        return;
      case kLdrsh:
        ldrsh(rt, MemOperandComputationHelper(cond, scratch, location, mask));
        return;
      default:
        VIXL_UNREACHABLE();
    }
    return;
  }

  Assembler::Delegate(type, instruction, cond, rt, location);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondRRL instruction,
                              Condition cond,
                              Register rt,
                              Register rt2,
                              Location* location) {
  VIXL_ASSERT(type == kLdrd);

  CONTEXT_SCOPE;

  if (location->IsBound()) {
    CodeBufferCheckScope scope(this, 6 * kMaxInstructionSizeInBytes);
    UseScratchRegisterScope temps(this);
    temps.Include(rt, rt2);
    Register scratch = temps.Acquire();
    uint32_t mask = GetOffsetMask(type, Offset);
    ldrd(rt, rt2, MemOperandComputationHelper(cond, scratch, location, mask));
    return;
  }

  Assembler::Delegate(type, instruction, cond, rt, rt2, location);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondSizeRMop instruction,
                              Condition cond,
                              EncodingSize size,
                              Register rd,
                              const MemOperand& operand) {
  CONTEXT_SCOPE;
  VIXL_ASSERT(size.IsBest());
  VIXL_ASSERT((type == kLdr) || (type == kLdrb) || (type == kLdrh) ||
              (type == kLdrsb) || (type == kLdrsh) || (type == kStr) ||
              (type == kStrb) || (type == kStrh));
  if (operand.IsImmediate()) {
    const Register& rn = operand.GetBaseRegister();
    AddrMode addrmode = operand.GetAddrMode();
    int32_t offset = operand.GetOffsetImmediate();
    uint32_t extra_offset_mask = GetOffsetMask(type, addrmode);
    // Try to maximize the offset used by the MemOperand (load_store_offset).
    // Add the part which can't be used by the MemOperand (add_offset).
    uint32_t load_store_offset = offset & extra_offset_mask;
    uint32_t add_offset = offset & ~extra_offset_mask;
    if ((add_offset != 0) &&
        (IsModifiedImmediate(offset) || IsModifiedImmediate(-offset))) {
      load_store_offset = 0;
      add_offset = offset;
    }
    switch (addrmode) {
      case PreIndex:
        // Avoid the unpredictable case 'str r0, [r0, imm]!'
        if (!rn.Is(rd)) {
          // Pre-Indexed case:
          // ldr r0, [r1, 12345]! will translate into
          //   add r1, r1, 12345
          //   ldr r0, [r1]
          {
            CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
            add(cond, rn, rn, add_offset);
          }
          {
            CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
            (this->*instruction)(cond,
                                 size,
                                 rd,
                                 MemOperand(rn, load_store_offset, PreIndex));
          }
          return;
        }
        break;
      case Offset: {
        UseScratchRegisterScope temps(this);
        // Allow using the destination as a scratch register if possible.
        if ((type != kStr) && (type != kStrb) && (type != kStrh) &&
            !rd.Is(rn)) {
          temps.Include(rd);
        }
        Register scratch = temps.Acquire();
        // Offset case:
        // ldr r0, [r1, 12345] will translate into
        //   add r0, r1, 12345
        //   ldr r0, [r0]
        {
          CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
          add(cond, scratch, rn, add_offset);
        }
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond,
                               size,
                               rd,
                               MemOperand(scratch, load_store_offset));
        }
        return;
      }
      case PostIndex:
        // Avoid the unpredictable case 'ldr r0, [r0], imm'
        if (!rn.Is(rd)) {
          // Post-indexed case:
          // ldr r0. [r1], imm32 will translate into
          //   ldr r0, [r1]
          //   movw ip. imm32 & 0xffffffff
          //   movt ip, imm32 >> 16
          //   add r1, r1, ip
          {
            CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
            (this->*instruction)(cond,
                                 size,
                                 rd,
                                 MemOperand(rn, load_store_offset, PostIndex));
          }
          {
            CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
            add(cond, rn, rn, add_offset);
          }
          return;
        }
        break;
    }
  } else if (operand.IsPlainRegister()) {
    const Register& rn = operand.GetBaseRegister();
    AddrMode addrmode = operand.GetAddrMode();
    const Register& rm = operand.GetOffsetRegister();
    if (rm.IsPC()) {
      VIXL_ABORT_WITH_MSG(
          "The MacroAssembler does not convert loads and stores with a PC "
          "offset register.\n");
    }
    if (rn.IsPC()) {
      if (addrmode == Offset) {
        if (IsUsingT32()) {
          VIXL_ABORT_WITH_MSG(
              "The MacroAssembler does not convert loads and stores with a PC "
              "base register for T32.\n");
        }
      } else {
        VIXL_ABORT_WITH_MSG(
            "The MacroAssembler does not convert loads and stores with a PC "
            "base register in pre-index or post-index mode.\n");
      }
    }
    switch (addrmode) {
      case PreIndex:
        // Avoid the unpredictable case 'str r0, [r0, imm]!'
        if (!rn.Is(rd)) {
          // Pre-Indexed case:
          // ldr r0, [r1, r2]! will translate into
          //   add r1, r1, r2
          //   ldr r0, [r1]
          {
            CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
            if (operand.GetSign().IsPlus()) {
              add(cond, rn, rn, rm);
            } else {
              sub(cond, rn, rn, rm);
            }
          }
          {
            CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
            (this->*instruction)(cond, size, rd, MemOperand(rn, Offset));
          }
          return;
        }
        break;
      case Offset: {
        UseScratchRegisterScope temps(this);
        // Allow using the destination as a scratch register if this is not a
        // store.
        // Avoid using PC as a temporary as this has side-effects.
        if ((type != kStr) && (type != kStrb) && (type != kStrh) &&
            !rd.IsPC()) {
          temps.Include(rd);
        }
        Register scratch = temps.Acquire();
        // Offset case:
        // ldr r0, [r1, r2] will translate into
        //   add r0, r1, r2
        //   ldr r0, [r0]
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          if (operand.GetSign().IsPlus()) {
            add(cond, scratch, rn, rm);
          } else {
            sub(cond, scratch, rn, rm);
          }
        }
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, size, rd, MemOperand(scratch, Offset));
        }
        return;
      }
      case PostIndex:
        // Avoid the unpredictable case 'ldr r0, [r0], imm'
        if (!rn.Is(rd)) {
          // Post-indexed case:
          // ldr r0. [r1], r2 will translate into
          //   ldr r0, [r1]
          //   add r1, r1, r2
          {
            CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
            (this->*instruction)(cond, size, rd, MemOperand(rn, Offset));
          }
          {
            CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
            if (operand.GetSign().IsPlus()) {
              add(cond, rn, rn, rm);
            } else {
              sub(cond, rn, rn, rm);
            }
          }
          return;
        }
        break;
    }
  }
  Assembler::Delegate(type, instruction, cond, size, rd, operand);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondRRMop instruction,
                              Condition cond,
                              Register rt,
                              Register rt2,
                              const MemOperand& operand) {
  if ((type == kLdaexd) || (type == kLdrexd) || (type == kStlex) ||
      (type == kStlexb) || (type == kStlexh) || (type == kStrex) ||
      (type == kStrexb) || (type == kStrexh)) {
    UnimplementedDelegate(type);
    return;
  }

  VIXL_ASSERT((type == kLdrd) || (type == kStrd));

  CONTEXT_SCOPE;

  // TODO: Should we allow these cases?
  if (IsUsingA32()) {
    // The first register needs to be even.
    if ((rt.GetCode() & 1) != 0) {
      UnimplementedDelegate(type);
      return;
    }
    // Registers need to be adjacent.
    if (((rt.GetCode() + 1) % kNumberOfRegisters) != rt2.GetCode()) {
      UnimplementedDelegate(type);
      return;
    }
    // LDRD lr, pc [...] is not allowed.
    if (rt.Is(lr)) {
      UnimplementedDelegate(type);
      return;
    }
  }

  if (operand.IsImmediate()) {
    const Register& rn = operand.GetBaseRegister();
    AddrMode addrmode = operand.GetAddrMode();
    int32_t offset = operand.GetOffsetImmediate();
    uint32_t extra_offset_mask = GetOffsetMask(type, addrmode);
    // Try to maximize the offset used by the MemOperand (load_store_offset).
    // Add the part which can't be used by the MemOperand (add_offset).
    uint32_t load_store_offset = offset & extra_offset_mask;
    uint32_t add_offset = offset & ~extra_offset_mask;
    if ((add_offset != 0) &&
        (IsModifiedImmediate(offset) || IsModifiedImmediate(-offset))) {
      load_store_offset = 0;
      add_offset = offset;
    }
    switch (addrmode) {
      case PreIndex: {
        // Allow using the destinations as a scratch registers if possible.
        UseScratchRegisterScope temps(this);
        if (type == kLdrd) {
          if (!rt.Is(rn)) temps.Include(rt);
          if (!rt2.Is(rn)) temps.Include(rt2);
        }

        // Pre-Indexed case:
        // ldrd r0, r1, [r2, 12345]! will translate into
        //   add r2, 12345
        //   ldrd r0, r1, [r2]
        {
          CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
          add(cond, rn, rn, add_offset);
        }
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond,
                               rt,
                               rt2,
                               MemOperand(rn, load_store_offset, PreIndex));
        }
        return;
      }
      case Offset: {
        UseScratchRegisterScope temps(this);
        // Allow using the destinations as a scratch registers if possible.
        if (type == kLdrd) {
          if (!rt.Is(rn)) temps.Include(rt);
          if (!rt2.Is(rn)) temps.Include(rt2);
        }
        Register scratch = temps.Acquire();
        // Offset case:
        // ldrd r0, r1, [r2, 12345] will translate into
        //   add r0, r2, 12345
        //   ldrd r0, r1, [r0]
        {
          CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
          add(cond, scratch, rn, add_offset);
        }
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond,
                               rt,
                               rt2,
                               MemOperand(scratch, load_store_offset));
        }
        return;
      }
      case PostIndex:
        // Avoid the unpredictable case 'ldrd r0, r1, [r0], imm'
        if (!rn.Is(rt) && !rn.Is(rt2)) {
          // Post-indexed case:
          // ldrd r0, r1, [r2], imm32 will translate into
          //   ldrd r0, r1, [r2]
          //   movw ip. imm32 & 0xffffffff
          //   movt ip, imm32 >> 16
          //   add r2, ip
          {
            CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
            (this->*instruction)(cond,
                                 rt,
                                 rt2,
                                 MemOperand(rn, load_store_offset, PostIndex));
          }
          {
            CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
            add(cond, rn, rn, add_offset);
          }
          return;
        }
        break;
    }
  }
  if (operand.IsPlainRegister()) {
    const Register& rn = operand.GetBaseRegister();
    const Register& rm = operand.GetOffsetRegister();
    AddrMode addrmode = operand.GetAddrMode();
    switch (addrmode) {
      case PreIndex:
        // ldrd r0, r1, [r2, r3]! will translate into
        //   add r2, r3
        //   ldrd r0, r1, [r2]
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          if (operand.GetSign().IsPlus()) {
            add(cond, rn, rn, rm);
          } else {
            sub(cond, rn, rn, rm);
          }
        }
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, rt, rt2, MemOperand(rn, Offset));
        }
        return;
      case PostIndex:
        // ldrd r0, r1, [r2], r3 will translate into
        //   ldrd r0, r1, [r2]
        //   add r2, r3
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, rt, rt2, MemOperand(rn, Offset));
        }
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          if (operand.GetSign().IsPlus()) {
            add(cond, rn, rn, rm);
          } else {
            sub(cond, rn, rn, rm);
          }
        }
        return;
      case Offset: {
        UseScratchRegisterScope temps(this);
        // Allow using the destinations as a scratch registers if possible.
        if (type == kLdrd) {
          if (!rt.Is(rn)) temps.Include(rt);
          if (!rt2.Is(rn)) temps.Include(rt2);
        }
        Register scratch = temps.Acquire();
        // Offset case:
        // ldrd r0, r1, [r2, r3] will translate into
        //   add r0, r2, r3
        //   ldrd r0, r1, [r0]
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          if (operand.GetSign().IsPlus()) {
            add(cond, scratch, rn, rm);
          } else {
            sub(cond, scratch, rn, rm);
          }
        }
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, rt, rt2, MemOperand(scratch, Offset));
        }
        return;
      }
    }
  }
  Assembler::Delegate(type, instruction, cond, rt, rt2, operand);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondDtSMop instruction,
                              Condition cond,
                              DataType dt,
                              SRegister rd,
                              const MemOperand& operand) {
  CONTEXT_SCOPE;
  if (operand.IsImmediate()) {
    const Register& rn = operand.GetBaseRegister();
    AddrMode addrmode = operand.GetAddrMode();
    int32_t offset = operand.GetOffsetImmediate();
    VIXL_ASSERT(((offset > 0) && operand.GetSign().IsPlus()) ||
                ((offset < 0) && operand.GetSign().IsMinus()) || (offset == 0));
    if (rn.IsPC()) {
      VIXL_ABORT_WITH_MSG(
          "The MacroAssembler does not convert vldr or vstr with a PC base "
          "register.\n");
    }
    switch (addrmode) {
      case PreIndex:
        // Pre-Indexed case:
        // vldr.32 s0, [r1, 12345]! will translate into
        //   add r1, 12345
        //   vldr.32 s0, [r1]
        if (offset != 0) {
          CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
          add(cond, rn, rn, offset);
        }
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, dt, rd, MemOperand(rn, Offset));
        }
        return;
      case Offset: {
        UseScratchRegisterScope temps(this);
        Register scratch = temps.Acquire();
        // Offset case:
        // vldr.32 s0, [r1, 12345] will translate into
        //   add ip, r1, 12345
        //   vldr.32 s0, [ip]
        {
          VIXL_ASSERT(offset != 0);
          CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
          add(cond, scratch, rn, offset);
        }
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, dt, rd, MemOperand(scratch, Offset));
        }
        return;
      }
      case PostIndex:
        // Post-indexed case:
        // vldr.32 s0, [r1], imm32 will translate into
        //   vldr.32 s0, [r1]
        //   movw ip. imm32 & 0xffffffff
        //   movt ip, imm32 >> 16
        //   add r1, ip
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, dt, rd, MemOperand(rn, Offset));
        }
        if (offset != 0) {
          CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
          add(cond, rn, rn, offset);
        }
        return;
    }
  }
  Assembler::Delegate(type, instruction, cond, dt, rd, operand);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondDtDMop instruction,
                              Condition cond,
                              DataType dt,
                              DRegister rd,
                              const MemOperand& operand) {
  CONTEXT_SCOPE;
  if (operand.IsImmediate()) {
    const Register& rn = operand.GetBaseRegister();
    AddrMode addrmode = operand.GetAddrMode();
    int32_t offset = operand.GetOffsetImmediate();
    VIXL_ASSERT(((offset > 0) && operand.GetSign().IsPlus()) ||
                ((offset < 0) && operand.GetSign().IsMinus()) || (offset == 0));
    if (rn.IsPC()) {
      VIXL_ABORT_WITH_MSG(
          "The MacroAssembler does not convert vldr or vstr with a PC base "
          "register.\n");
    }
    switch (addrmode) {
      case PreIndex:
        // Pre-Indexed case:
        // vldr.64 d0, [r1, 12345]! will translate into
        //   add r1, 12345
        //   vldr.64 d0, [r1]
        if (offset != 0) {
          CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
          add(cond, rn, rn, offset);
        }
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, dt, rd, MemOperand(rn, Offset));
        }
        return;
      case Offset: {
        UseScratchRegisterScope temps(this);
        Register scratch = temps.Acquire();
        // Offset case:
        // vldr.64 d0, [r1, 12345] will translate into
        //   add ip, r1, 12345
        //   vldr.32 s0, [ip]
        {
          VIXL_ASSERT(offset != 0);
          CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
          add(cond, scratch, rn, offset);
        }
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, dt, rd, MemOperand(scratch, Offset));
        }
        return;
      }
      case PostIndex:
        // Post-indexed case:
        // vldr.64 d0. [r1], imm32 will translate into
        //   vldr.64 d0, [r1]
        //   movw ip. imm32 & 0xffffffff
        //   movt ip, imm32 >> 16
        //   add r1, ip
        {
          CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
          (this->*instruction)(cond, dt, rd, MemOperand(rn, Offset));
        }
        if (offset != 0) {
          CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes);
          add(cond, rn, rn, offset);
        }
        return;
    }
  }
  Assembler::Delegate(type, instruction, cond, dt, rd, operand);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondMsrOp instruction,
                              Condition cond,
                              MaskedSpecialRegister spec_reg,
                              const Operand& operand) {
  USE(type);
  VIXL_ASSERT(type == kMsr);
  if (operand.IsImmediate()) {
    UseScratchRegisterScope temps(this);
    Register scratch = temps.Acquire();
    {
      CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes);
      mov(cond, scratch, operand);
    }
    CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes);
    msr(cond, spec_reg, scratch);
    return;
  }
  Assembler::Delegate(type, instruction, cond, spec_reg, operand);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondDtDL instruction,
                              Condition cond,
                              DataType dt,
                              DRegister rd,
                              Location* location) {
  VIXL_ASSERT(type == kVldr);

  CONTEXT_SCOPE;

  if (location->IsBound()) {
    CodeBufferCheckScope scope(this, 5 * kMaxInstructionSizeInBytes);
    UseScratchRegisterScope temps(this);
    Register scratch = temps.Acquire();
    uint32_t mask = GetOffsetMask(type, Offset);
    vldr(dt, rd, MemOperandComputationHelper(cond, scratch, location, mask));
    return;
  }

  Assembler::Delegate(type, instruction, cond, dt, rd, location);
}


void MacroAssembler::Delegate(InstructionType type,
                              InstructionCondDtSL instruction,
                              Condition cond,
                              DataType dt,
                              SRegister rd,
                              Location* location) {
  VIXL_ASSERT(type == kVldr);

  CONTEXT_SCOPE;

  if (location->IsBound()) {
    CodeBufferCheckScope scope(this, 5 * kMaxInstructionSizeInBytes);
    UseScratchRegisterScope temps(this);
    Register scratch = temps.Acquire();
    uint32_t mask = GetOffsetMask(type, Offset);
    vldr(dt, rd, MemOperandComputationHelper(cond, scratch, location, mask));
    return;
  }

  Assembler::Delegate(type, instruction, cond, dt, rd, location);
}


#undef CONTEXT_SCOPE
#undef TOSTRING
#undef STRINGIFY

// Start of generated code.
// End of generated code.
}  // namespace aarch32
}  // namespace vixl
