/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <dirent.h>
#include <errno.h>
#include <fstream>
#include <map>
#include <string.h>
#include <sys/types.h>

#include "gtest/gtest.h"
#include "utils/arm/assembler_thumb2.h"

#include "jni/quick/calling_convention.h"
#include "utils/arm/jni_macro_assembler_arm_vixl.h"

#include "base/hex_dump.h"
#include "common_runtime_test.h"

namespace art {
namespace arm {

// Include results file (generated manually)
#include "assembler_thumb_test_expected.cc.inc"

#ifndef ART_TARGET_ANDROID
// This controls whether the results are printed to the
// screen or compared against the expected output.
// To generate new expected output, set this to true and
// copy the output into the .cc.inc file in the form
// of the other results.
//
// When this is false, the results are not printed to the
// output, but are compared against the expected results
// in the .cc.inc file.
static constexpr bool kPrintResults = false;
#endif

void SetAndroidData() {
  const char* data = getenv("ANDROID_DATA");
  if (data == nullptr) {
    setenv("ANDROID_DATA", "/tmp", 1);
  }
}

int CompareIgnoringSpace(const char* s1, const char* s2) {
  while (*s1 != '\0') {
    while (isspace(*s1)) ++s1;
    while (isspace(*s2)) ++s2;
    if (*s1 == '\0' || *s1 != *s2) {
      break;
    }
    ++s1;
    ++s2;
  }
  return *s1 - *s2;
}

void InitResults() {
  if (test_results.empty()) {
    setup_results();
  }
}

std::string GetToolsDir() {
#ifndef ART_TARGET_ANDROID
  // This will only work on the host.  There is no as, objcopy or objdump on the device.
  static std::string toolsdir;

  if (toolsdir.empty()) {
    setup_results();
    toolsdir = CommonRuntimeTest::GetAndroidTargetToolsDir(kThumb2);
    SetAndroidData();
  }

  return toolsdir;
#else
  return std::string();
#endif
}

void DumpAndCheck(std::vector<uint8_t>& code, const char* testname, const char* const* results) {
#ifndef ART_TARGET_ANDROID
  static std::string toolsdir = GetToolsDir();

  ScratchFile file;

  const char* filename = file.GetFilename().c_str();

  std::ofstream out(filename);
  if (out) {
    out << ".section \".text\"\n";
    out << ".syntax unified\n";
    out << ".arch armv7-a\n";
    out << ".thumb\n";
    out << ".thumb_func\n";
    out << ".type " << testname << ", #function\n";
    out << ".global " << testname << "\n";
    out << testname << ":\n";
    out << ".fnstart\n";

    for (uint32_t i = 0 ; i < code.size(); ++i) {
      out << ".byte " << (static_cast<int>(code[i]) & 0xff) << "\n";
    }
    out << ".fnend\n";
    out << ".size " << testname << ", .-" << testname << "\n";
  }
  out.close();

  char cmd[1024];

  // Assemble the .S
  snprintf(cmd, sizeof(cmd), "%sas %s -o %s.o", toolsdir.c_str(), filename, filename);
  int cmd_result = system(cmd);
  ASSERT_EQ(cmd_result, 0) << strerror(errno);

  // Remove the $d symbols to prevent the disassembler dumping the instructions
  // as .word
  snprintf(cmd, sizeof(cmd), "%sobjcopy -N '$d' %s.o %s.oo", toolsdir.c_str(), filename, filename);
  int cmd_result2 = system(cmd);
  ASSERT_EQ(cmd_result2, 0) << strerror(errno);

  // Disassemble.

  snprintf(cmd, sizeof(cmd), "%sobjdump -d %s.oo | grep '^  *[0-9a-f][0-9a-f]*:'",
    toolsdir.c_str(), filename);
  if (kPrintResults) {
    // Print the results only, don't check. This is used to generate new output for inserting
    // into the .inc file, so let's add the appropriate prefix/suffix needed in the C++ code.
    strcat(cmd, " | sed '-es/^/  \"/' | sed '-es/$/\\\\n\",/'");
    int cmd_result3 = system(cmd);
    ASSERT_EQ(cmd_result3, 0) << strerror(errno);
  } else {
    // Check the results match the appropriate results in the .inc file.
    FILE *fp = popen(cmd, "r");
    ASSERT_TRUE(fp != nullptr);

    uint32_t lineindex = 0;

    while (!feof(fp)) {
      char testline[256];
      char *s = fgets(testline, sizeof(testline), fp);
      if (s == nullptr) {
        break;
      }
      if (CompareIgnoringSpace(results[lineindex], testline) != 0) {
        LOG(FATAL) << "Output is not as expected at line: " << lineindex
          << results[lineindex] << "/" << testline << ", test name: " << testname;
      }
      ++lineindex;
    }
    // Check that we are at the end.
    ASSERT_TRUE(results[lineindex] == nullptr);
    fclose(fp);
  }

  char buf[FILENAME_MAX];
  snprintf(buf, sizeof(buf), "%s.o", filename);
  unlink(buf);

  snprintf(buf, sizeof(buf), "%s.oo", filename);
  unlink(buf);
#endif  // ART_TARGET_ANDROID
}

#define __ assembler->

void EmitAndCheck(arm::Thumb2Assembler* assembler, const char* testname,
                  const char* const* results) {
  __ FinalizeCode();
  size_t cs = __ CodeSize();
  std::vector<uint8_t> managed_code(cs);
  MemoryRegion code(&managed_code[0], managed_code.size());
  __ FinalizeInstructions(code);

  DumpAndCheck(managed_code, testname, results);
}

void EmitAndCheck(arm::Thumb2Assembler* assembler, const char* testname) {
  InitResults();
  std::map<std::string, const char* const*>::iterator results = test_results.find(testname);
  ASSERT_NE(results, test_results.end());

  EmitAndCheck(assembler, testname, results->second);
}

#undef __

class Thumb2AssemblerTest : public ::testing::Test {
 public:
  Thumb2AssemblerTest() : pool(), arena(&pool), assembler(&arena) { }

  ArenaPool pool;
  ArenaAllocator arena;
  arm::Thumb2Assembler assembler;
};

#define __ assembler.

TEST_F(Thumb2AssemblerTest, SimpleMov) {
  __ movs(R0, ShifterOperand(R1));
  __ mov(R0, ShifterOperand(R1));
  __ mov(R8, ShifterOperand(R9));

  __ mov(R0, ShifterOperand(1));
  __ mov(R8, ShifterOperand(9));

  EmitAndCheck(&assembler, "SimpleMov");
}

TEST_F(Thumb2AssemblerTest, SimpleMov32) {
  __ Force32Bit();

  __ mov(R0, ShifterOperand(R1));
  __ mov(R8, ShifterOperand(R9));

  EmitAndCheck(&assembler, "SimpleMov32");
}

TEST_F(Thumb2AssemblerTest, SimpleMovAdd) {
  __ mov(R0, ShifterOperand(R1));
  __ adds(R0, R1, ShifterOperand(R2));
  __ add(R0, R1, ShifterOperand(0));

  EmitAndCheck(&assembler, "SimpleMovAdd");
}

TEST_F(Thumb2AssemblerTest, DataProcessingRegister) {
  // 32 bit variants using low registers.
  __ mvn(R0, ShifterOperand(R1), AL, kCcKeep);
  __ add(R0, R1, ShifterOperand(R2), AL, kCcKeep);
  __ sub(R0, R1, ShifterOperand(R2), AL, kCcKeep);
  __ and_(R0, R1, ShifterOperand(R2), AL, kCcKeep);
  __ orr(R0, R1, ShifterOperand(R2), AL, kCcKeep);
  __ orn(R0, R1, ShifterOperand(R2), AL, kCcKeep);
  __ eor(R0, R1, ShifterOperand(R2), AL, kCcKeep);
  __ bic(R0, R1, ShifterOperand(R2), AL, kCcKeep);
  __ adc(R0, R1, ShifterOperand(R2), AL, kCcKeep);
  __ sbc(R0, R1, ShifterOperand(R2), AL, kCcKeep);
  __ rsb(R0, R1, ShifterOperand(R2), AL, kCcKeep);
  __ teq(R0, ShifterOperand(R1));

  // 16 bit variants using low registers.
  __ movs(R0, ShifterOperand(R1));
  __ mov(R0, ShifterOperand(R1), AL, kCcKeep);
  __ mvns(R0, ShifterOperand(R1));
  __ add(R0, R0, ShifterOperand(R1), AL, kCcKeep);
  __ adds(R0, R1, ShifterOperand(R2));
  __ subs(R0, R1, ShifterOperand(R2));
  __ adcs(R0, R0, ShifterOperand(R1));
  __ sbcs(R0, R0, ShifterOperand(R1));
  __ ands(R0, R0, ShifterOperand(R1));
  __ orrs(R0, R0, ShifterOperand(R1));
  __ eors(R0, R0, ShifterOperand(R1));
  __ bics(R0, R0, ShifterOperand(R1));
  __ tst(R0, ShifterOperand(R1));
  __ cmp(R0, ShifterOperand(R1));
  __ cmn(R0, ShifterOperand(R1));

  // 16-bit variants using high registers.
  __ mov(R1, ShifterOperand(R8), AL, kCcKeep);
  __ mov(R9, ShifterOperand(R0), AL, kCcKeep);
  __ mov(R8, ShifterOperand(R9), AL, kCcKeep);
  __ add(R1, R1, ShifterOperand(R8), AL, kCcKeep);
  __ add(R9, R9, ShifterOperand(R0), AL, kCcKeep);
  __ add(R8, R8, ShifterOperand(R9), AL, kCcKeep);
  __ cmp(R0, ShifterOperand(R9));
  __ cmp(R8, ShifterOperand(R1));
  __ cmp(R9, ShifterOperand(R8));

  // The 16-bit RSBS Rd, Rn, #0, also known as NEGS Rd, Rn is specified using
  // an immediate (0) but emitted without any, so we test it here.
  __ rsbs(R0, R1, ShifterOperand(0));
  __ rsbs(R0, R0, ShifterOperand(0));  // Check Rd == Rn code path.

  // 32 bit variants using high registers that would be 16-bit if using low registers.
  __ movs(R0, ShifterOperand(R8));
  __ mvns(R0, ShifterOperand(R8));
  __ add(R0, R1, ShifterOperand(R8), AL, kCcKeep);
  __ adds(R0, R1, ShifterOperand(R8));
  __ subs(R0, R1, ShifterOperand(R8));
  __ adcs(R0, R0, ShifterOperand(R8));
  __ sbcs(R0, R0, ShifterOperand(R8));
  __ ands(R0, R0, ShifterOperand(R8));
  __ orrs(R0, R0, ShifterOperand(R8));
  __ eors(R0, R0, ShifterOperand(R8));
  __ bics(R0, R0, ShifterOperand(R8));
  __ tst(R0, ShifterOperand(R8));
  __ cmn(R0, ShifterOperand(R8));
  __ rsbs(R0, R8, ShifterOperand(0));  // Check that this is not emitted as 16-bit.
  __ rsbs(R8, R8, ShifterOperand(0));  // Check that this is not emitted as 16-bit (Rd == Rn).

  // 32-bit variants of instructions that would be 16-bit outside IT block.
  __ it(arm::EQ);
  __ mvns(R0, ShifterOperand(R1), arm::EQ);
  __ it(arm::EQ);
  __ adds(R0, R1, ShifterOperand(R2), arm::EQ);
  __ it(arm::EQ);
  __ subs(R0, R1, ShifterOperand(R2), arm::EQ);
  __ it(arm::EQ);
  __ adcs(R0, R0, ShifterOperand(R1), arm::EQ);
  __ it(arm::EQ);
  __ sbcs(R0, R0, ShifterOperand(R1), arm::EQ);
  __ it(arm::EQ);
  __ ands(R0, R0, ShifterOperand(R1), arm::EQ);
  __ it(arm::EQ);
  __ orrs(R0, R0, ShifterOperand(R1), arm::EQ);
  __ it(arm::EQ);
  __ eors(R0, R0, ShifterOperand(R1), arm::EQ);
  __ it(arm::EQ);
  __ bics(R0, R0, ShifterOperand(R1), arm::EQ);

  // 16-bit variants of instructions that would be 32-bit outside IT block.
  __ it(arm::EQ);
  __ mvn(R0, ShifterOperand(R1), arm::EQ, kCcKeep);
  __ it(arm::EQ);
  __ add(R0, R1, ShifterOperand(R2), arm::EQ, kCcKeep);
  __ it(arm::EQ);
  __ sub(R0, R1, ShifterOperand(R2), arm::EQ, kCcKeep);
  __ it(arm::EQ);
  __ adc(R0, R0, ShifterOperand(R1), arm::EQ, kCcKeep);
  __ it(arm::EQ);
  __ sbc(R0, R0, ShifterOperand(R1), arm::EQ, kCcKeep);
  __ it(arm::EQ);
  __ and_(R0, R0, ShifterOperand(R1), arm::EQ, kCcKeep);
  __ it(arm::EQ);
  __ orr(R0, R0, ShifterOperand(R1), arm::EQ, kCcKeep);
  __ it(arm::EQ);
  __ eor(R0, R0, ShifterOperand(R1), arm::EQ, kCcKeep);
  __ it(arm::EQ);
  __ bic(R0, R0, ShifterOperand(R1), arm::EQ, kCcKeep);

  // 16 bit variants selected for the default kCcDontCare.
  __ mov(R0, ShifterOperand(R1));
  __ mvn(R0, ShifterOperand(R1));
  __ add(R0, R0, ShifterOperand(R1));
  __ add(R0, R1, ShifterOperand(R2));
  __ sub(R0, R1, ShifterOperand(R2));
  __ adc(R0, R0, ShifterOperand(R1));
  __ sbc(R0, R0, ShifterOperand(R1));
  __ and_(R0, R0, ShifterOperand(R1));
  __ orr(R0, R0, ShifterOperand(R1));
  __ eor(R0, R0, ShifterOperand(R1));
  __ bic(R0, R0, ShifterOperand(R1));
  __ mov(R1, ShifterOperand(R8));
  __ mov(R9, ShifterOperand(R0));
  __ mov(R8, ShifterOperand(R9));
  __ add(R1, R1, ShifterOperand(R8));
  __ add(R9, R9, ShifterOperand(R0));
  __ add(R8, R8, ShifterOperand(R9));
  __ rsb(R0, R1, ShifterOperand(0));
  __ rsb(R0, R0, ShifterOperand(0));

  // And an arbitrary 32-bit instruction using IP.
  __ add(R12, R1, ShifterOperand(R0), AL, kCcKeep);

  EmitAndCheck(&assembler, "DataProcessingRegister");
}

TEST_F(Thumb2AssemblerTest, DataProcessingImmediate) {
  __ mov(R0, ShifterOperand(0x55));
  __ mvn(R0, ShifterOperand(0x55));
  __ add(R0, R1, ShifterOperand(0x55));
  __ sub(R0, R1, ShifterOperand(0x55));
  __ and_(R0, R1, ShifterOperand(0x55));
  __ orr(R0, R1, ShifterOperand(0x55));
  __ orn(R0, R1, ShifterOperand(0x55));
  __ eor(R0, R1, ShifterOperand(0x55));
  __ bic(R0, R1, ShifterOperand(0x55));
  __ adc(R0, R1, ShifterOperand(0x55));
  __ sbc(R0, R1, ShifterOperand(0x55));
  __ rsb(R0, R1, ShifterOperand(0x55));

  __ tst(R0, ShifterOperand(0x55));
  __ teq(R0, ShifterOperand(0x55));
  __ cmp(R0, ShifterOperand(0x55));
  __ cmn(R0, ShifterOperand(0x55));

  __ add(R0, R1, ShifterOperand(5));
  __ sub(R0, R1, ShifterOperand(5));

  __ movs(R0, ShifterOperand(0x55));
  __ mvns(R0, ShifterOperand(0x55));

  __ adds(R0, R1, ShifterOperand(5));
  __ subs(R0, R1, ShifterOperand(5));

  EmitAndCheck(&assembler, "DataProcessingImmediate");
}

TEST_F(Thumb2AssemblerTest, DataProcessingModifiedImmediate) {
  __ mov(R0, ShifterOperand(0x550055));
  __ mvn(R0, ShifterOperand(0x550055));
  __ add(R0, R1, ShifterOperand(0x550055));
  __ sub(R0, R1, ShifterOperand(0x550055));
  __ and_(R0, R1, ShifterOperand(0x550055));
  __ orr(R0, R1, ShifterOperand(0x550055));
  __ orn(R0, R1, ShifterOperand(0x550055));
  __ eor(R0, R1, ShifterOperand(0x550055));
  __ bic(R0, R1, ShifterOperand(0x550055));
  __ adc(R0, R1, ShifterOperand(0x550055));
  __ sbc(R0, R1, ShifterOperand(0x550055));
  __ rsb(R0, R1, ShifterOperand(0x550055));

  __ tst(R0, ShifterOperand(0x550055));
  __ teq(R0, ShifterOperand(0x550055));
  __ cmp(R0, ShifterOperand(0x550055));
  __ cmn(R0, ShifterOperand(0x550055));

  EmitAndCheck(&assembler, "DataProcessingModifiedImmediate");
}


TEST_F(Thumb2AssemblerTest, DataProcessingModifiedImmediates) {
  __ mov(R0, ShifterOperand(0x550055));
  __ mov(R0, ShifterOperand(0x55005500));
  __ mov(R0, ShifterOperand(0x55555555));
  __ mov(R0, ShifterOperand(0xd5000000));       // rotated to first position
  __ mov(R0, ShifterOperand(0x6a000000));       // rotated to second position
  __ mov(R0, ShifterOperand(0x350));            // rotated to 2nd last position
  __ mov(R0, ShifterOperand(0x1a8));            // rotated to last position

  EmitAndCheck(&assembler, "DataProcessingModifiedImmediates");
}

TEST_F(Thumb2AssemblerTest, DataProcessingShiftedRegister) {
  // 16-bit variants.
  __ movs(R3, ShifterOperand(R4, LSL, 4));
  __ movs(R3, ShifterOperand(R4, LSR, 5));
  __ movs(R3, ShifterOperand(R4, ASR, 6));

  // 32-bit ROR because ROR immediate doesn't have the same 16-bit version as other shifts.
  __ movs(R3, ShifterOperand(R4, ROR, 7));

  // 32-bit RRX because RRX has no 16-bit version.
  __ movs(R3, ShifterOperand(R4, RRX));

  // 32 bit variants (not setting condition codes).
  __ mov(R3, ShifterOperand(R4, LSL, 4), AL, kCcKeep);
  __ mov(R3, ShifterOperand(R4, LSR, 5), AL, kCcKeep);
  __ mov(R3, ShifterOperand(R4, ASR, 6), AL, kCcKeep);
  __ mov(R3, ShifterOperand(R4, ROR, 7), AL, kCcKeep);
  __ mov(R3, ShifterOperand(R4, RRX), AL, kCcKeep);

  // 32 bit variants (high registers).
  __ movs(R8, ShifterOperand(R4, LSL, 4));
  __ movs(R8, ShifterOperand(R4, LSR, 5));
  __ movs(R8, ShifterOperand(R4, ASR, 6));
  __ movs(R8, ShifterOperand(R4, ROR, 7));
  __ movs(R8, ShifterOperand(R4, RRX));

  EmitAndCheck(&assembler, "DataProcessingShiftedRegister");
}

TEST_F(Thumb2AssemblerTest, ShiftImmediate) {
  // Note: This test produces the same results as DataProcessingShiftedRegister
  // but it does so using shift functions instead of mov().

  // 16-bit variants.
  __ Lsl(R3, R4, 4);
  __ Lsr(R3, R4, 5);
  __ Asr(R3, R4, 6);

  // 32-bit ROR because ROR immediate doesn't have the same 16-bit version as other shifts.
  __ Ror(R3, R4, 7);

  // 32-bit RRX because RRX has no 16-bit version.
  __ Rrx(R3, R4);

  // 32 bit variants (not setting condition codes).
  __ Lsl(R3, R4, 4, AL, kCcKeep);
  __ Lsr(R3, R4, 5, AL, kCcKeep);
  __ Asr(R3, R4, 6, AL, kCcKeep);
  __ Ror(R3, R4, 7, AL, kCcKeep);
  __ Rrx(R3, R4, AL, kCcKeep);

  // 32 bit variants (high registers).
  __ Lsls(R8, R4, 4);
  __ Lsrs(R8, R4, 5);
  __ Asrs(R8, R4, 6);
  __ Rors(R8, R4, 7);
  __ Rrxs(R8, R4);

  EmitAndCheck(&assembler, "ShiftImmediate");
}

TEST_F(Thumb2AssemblerTest, BasicLoad) {
  __ ldr(R3, Address(R4, 24));
  __ ldrb(R3, Address(R4, 24));
  __ ldrh(R3, Address(R4, 24));
  __ ldrsb(R3, Address(R4, 24));
  __ ldrsh(R3, Address(R4, 24));

  __ ldr(R3, Address(SP, 24));

  // 32 bit variants
  __ ldr(R8, Address(R4, 24));
  __ ldrb(R8, Address(R4, 24));
  __ ldrh(R8, Address(R4, 24));
  __ ldrsb(R8, Address(R4, 24));
  __ ldrsh(R8, Address(R4, 24));

  EmitAndCheck(&assembler, "BasicLoad");
}


TEST_F(Thumb2AssemblerTest, BasicStore) {
  __ str(R3, Address(R4, 24));
  __ strb(R3, Address(R4, 24));
  __ strh(R3, Address(R4, 24));

  __ str(R3, Address(SP, 24));

  // 32 bit variants.
  __ str(R8, Address(R4, 24));
  __ strb(R8, Address(R4, 24));
  __ strh(R8, Address(R4, 24));

  EmitAndCheck(&assembler, "BasicStore");
}

TEST_F(Thumb2AssemblerTest, ComplexLoad) {
  __ ldr(R3, Address(R4, 24, Address::Mode::Offset));
  __ ldr(R3, Address(R4, 24, Address::Mode::PreIndex));
  __ ldr(R3, Address(R4, 24, Address::Mode::PostIndex));
  __ ldr(R3, Address(R4, 24, Address::Mode::NegOffset));
  __ ldr(R3, Address(R4, 24, Address::Mode::NegPreIndex));
  __ ldr(R3, Address(R4, 24, Address::Mode::NegPostIndex));

  __ ldrb(R3, Address(R4, 24, Address::Mode::Offset));
  __ ldrb(R3, Address(R4, 24, Address::Mode::PreIndex));
  __ ldrb(R3, Address(R4, 24, Address::Mode::PostIndex));
  __ ldrb(R3, Address(R4, 24, Address::Mode::NegOffset));
  __ ldrb(R3, Address(R4, 24, Address::Mode::NegPreIndex));
  __ ldrb(R3, Address(R4, 24, Address::Mode::NegPostIndex));

  __ ldrh(R3, Address(R4, 24, Address::Mode::Offset));
  __ ldrh(R3, Address(R4, 24, Address::Mode::PreIndex));
  __ ldrh(R3, Address(R4, 24, Address::Mode::PostIndex));
  __ ldrh(R3, Address(R4, 24, Address::Mode::NegOffset));
  __ ldrh(R3, Address(R4, 24, Address::Mode::NegPreIndex));
  __ ldrh(R3, Address(R4, 24, Address::Mode::NegPostIndex));

  __ ldrsb(R3, Address(R4, 24, Address::Mode::Offset));
  __ ldrsb(R3, Address(R4, 24, Address::Mode::PreIndex));
  __ ldrsb(R3, Address(R4, 24, Address::Mode::PostIndex));
  __ ldrsb(R3, Address(R4, 24, Address::Mode::NegOffset));
  __ ldrsb(R3, Address(R4, 24, Address::Mode::NegPreIndex));
  __ ldrsb(R3, Address(R4, 24, Address::Mode::NegPostIndex));

  __ ldrsh(R3, Address(R4, 24, Address::Mode::Offset));
  __ ldrsh(R3, Address(R4, 24, Address::Mode::PreIndex));
  __ ldrsh(R3, Address(R4, 24, Address::Mode::PostIndex));
  __ ldrsh(R3, Address(R4, 24, Address::Mode::NegOffset));
  __ ldrsh(R3, Address(R4, 24, Address::Mode::NegPreIndex));
  __ ldrsh(R3, Address(R4, 24, Address::Mode::NegPostIndex));

  EmitAndCheck(&assembler, "ComplexLoad");
}


TEST_F(Thumb2AssemblerTest, ComplexStore) {
  __ str(R3, Address(R4, 24, Address::Mode::Offset));
  __ str(R3, Address(R4, 24, Address::Mode::PreIndex));
  __ str(R3, Address(R4, 24, Address::Mode::PostIndex));
  __ str(R3, Address(R4, 24, Address::Mode::NegOffset));
  __ str(R3, Address(R4, 24, Address::Mode::NegPreIndex));
  __ str(R3, Address(R4, 24, Address::Mode::NegPostIndex));

  __ strb(R3, Address(R4, 24, Address::Mode::Offset));
  __ strb(R3, Address(R4, 24, Address::Mode::PreIndex));
  __ strb(R3, Address(R4, 24, Address::Mode::PostIndex));
  __ strb(R3, Address(R4, 24, Address::Mode::NegOffset));
  __ strb(R3, Address(R4, 24, Address::Mode::NegPreIndex));
  __ strb(R3, Address(R4, 24, Address::Mode::NegPostIndex));

  __ strh(R3, Address(R4, 24, Address::Mode::Offset));
  __ strh(R3, Address(R4, 24, Address::Mode::PreIndex));
  __ strh(R3, Address(R4, 24, Address::Mode::PostIndex));
  __ strh(R3, Address(R4, 24, Address::Mode::NegOffset));
  __ strh(R3, Address(R4, 24, Address::Mode::NegPreIndex));
  __ strh(R3, Address(R4, 24, Address::Mode::NegPostIndex));

  EmitAndCheck(&assembler, "ComplexStore");
}

TEST_F(Thumb2AssemblerTest, NegativeLoadStore) {
  __ ldr(R3, Address(R4, -24, Address::Mode::Offset));
  __ ldr(R3, Address(R4, -24, Address::Mode::PreIndex));
  __ ldr(R3, Address(R4, -24, Address::Mode::PostIndex));
  __ ldr(R3, Address(R4, -24, Address::Mode::NegOffset));
  __ ldr(R3, Address(R4, -24, Address::Mode::NegPreIndex));
  __ ldr(R3, Address(R4, -24, Address::Mode::NegPostIndex));

  __ ldrb(R3, Address(R4, -24, Address::Mode::Offset));
  __ ldrb(R3, Address(R4, -24, Address::Mode::PreIndex));
  __ ldrb(R3, Address(R4, -24, Address::Mode::PostIndex));
  __ ldrb(R3, Address(R4, -24, Address::Mode::NegOffset));
  __ ldrb(R3, Address(R4, -24, Address::Mode::NegPreIndex));
  __ ldrb(R3, Address(R4, -24, Address::Mode::NegPostIndex));

  __ ldrh(R3, Address(R4, -24, Address::Mode::Offset));
  __ ldrh(R3, Address(R4, -24, Address::Mode::PreIndex));
  __ ldrh(R3, Address(R4, -24, Address::Mode::PostIndex));
  __ ldrh(R3, Address(R4, -24, Address::Mode::NegOffset));
  __ ldrh(R3, Address(R4, -24, Address::Mode::NegPreIndex));
  __ ldrh(R3, Address(R4, -24, Address::Mode::NegPostIndex));

  __ ldrsb(R3, Address(R4, -24, Address::Mode::Offset));
  __ ldrsb(R3, Address(R4, -24, Address::Mode::PreIndex));
  __ ldrsb(R3, Address(R4, -24, Address::Mode::PostIndex));
  __ ldrsb(R3, Address(R4, -24, Address::Mode::NegOffset));
  __ ldrsb(R3, Address(R4, -24, Address::Mode::NegPreIndex));
  __ ldrsb(R3, Address(R4, -24, Address::Mode::NegPostIndex));

  __ ldrsh(R3, Address(R4, -24, Address::Mode::Offset));
  __ ldrsh(R3, Address(R4, -24, Address::Mode::PreIndex));
  __ ldrsh(R3, Address(R4, -24, Address::Mode::PostIndex));
  __ ldrsh(R3, Address(R4, -24, Address::Mode::NegOffset));
  __ ldrsh(R3, Address(R4, -24, Address::Mode::NegPreIndex));
  __ ldrsh(R3, Address(R4, -24, Address::Mode::NegPostIndex));

  __ str(R3, Address(R4, -24, Address::Mode::Offset));
  __ str(R3, Address(R4, -24, Address::Mode::PreIndex));
  __ str(R3, Address(R4, -24, Address::Mode::PostIndex));
  __ str(R3, Address(R4, -24, Address::Mode::NegOffset));
  __ str(R3, Address(R4, -24, Address::Mode::NegPreIndex));
  __ str(R3, Address(R4, -24, Address::Mode::NegPostIndex));

  __ strb(R3, Address(R4, -24, Address::Mode::Offset));
  __ strb(R3, Address(R4, -24, Address::Mode::PreIndex));
  __ strb(R3, Address(R4, -24, Address::Mode::PostIndex));
  __ strb(R3, Address(R4, -24, Address::Mode::NegOffset));
  __ strb(R3, Address(R4, -24, Address::Mode::NegPreIndex));
  __ strb(R3, Address(R4, -24, Address::Mode::NegPostIndex));

  __ strh(R3, Address(R4, -24, Address::Mode::Offset));
  __ strh(R3, Address(R4, -24, Address::Mode::PreIndex));
  __ strh(R3, Address(R4, -24, Address::Mode::PostIndex));
  __ strh(R3, Address(R4, -24, Address::Mode::NegOffset));
  __ strh(R3, Address(R4, -24, Address::Mode::NegPreIndex));
  __ strh(R3, Address(R4, -24, Address::Mode::NegPostIndex));

  EmitAndCheck(&assembler, "NegativeLoadStore");
}

TEST_F(Thumb2AssemblerTest, SimpleLoadStoreDual) {
  __ strd(R2, Address(R0, 24, Address::Mode::Offset));
  __ ldrd(R2, Address(R0, 24, Address::Mode::Offset));

  EmitAndCheck(&assembler, "SimpleLoadStoreDual");
}

TEST_F(Thumb2AssemblerTest, ComplexLoadStoreDual) {
  __ strd(R2, Address(R0, 24, Address::Mode::Offset));
  __ strd(R2, Address(R0, 24, Address::Mode::PreIndex));
  __ strd(R2, Address(R0, 24, Address::Mode::PostIndex));
  __ strd(R2, Address(R0, 24, Address::Mode::NegOffset));
  __ strd(R2, Address(R0, 24, Address::Mode::NegPreIndex));
  __ strd(R2, Address(R0, 24, Address::Mode::NegPostIndex));

  __ ldrd(R2, Address(R0, 24, Address::Mode::Offset));
  __ ldrd(R2, Address(R0, 24, Address::Mode::PreIndex));
  __ ldrd(R2, Address(R0, 24, Address::Mode::PostIndex));
  __ ldrd(R2, Address(R0, 24, Address::Mode::NegOffset));
  __ ldrd(R2, Address(R0, 24, Address::Mode::NegPreIndex));
  __ ldrd(R2, Address(R0, 24, Address::Mode::NegPostIndex));

  EmitAndCheck(&assembler, "ComplexLoadStoreDual");
}

TEST_F(Thumb2AssemblerTest, NegativeLoadStoreDual) {
  __ strd(R2, Address(R0, -24, Address::Mode::Offset));
  __ strd(R2, Address(R0, -24, Address::Mode::PreIndex));
  __ strd(R2, Address(R0, -24, Address::Mode::PostIndex));
  __ strd(R2, Address(R0, -24, Address::Mode::NegOffset));
  __ strd(R2, Address(R0, -24, Address::Mode::NegPreIndex));
  __ strd(R2, Address(R0, -24, Address::Mode::NegPostIndex));

  __ ldrd(R2, Address(R0, -24, Address::Mode::Offset));
  __ ldrd(R2, Address(R0, -24, Address::Mode::PreIndex));
  __ ldrd(R2, Address(R0, -24, Address::Mode::PostIndex));
  __ ldrd(R2, Address(R0, -24, Address::Mode::NegOffset));
  __ ldrd(R2, Address(R0, -24, Address::Mode::NegPreIndex));
  __ ldrd(R2, Address(R0, -24, Address::Mode::NegPostIndex));

  EmitAndCheck(&assembler, "NegativeLoadStoreDual");
}

TEST_F(Thumb2AssemblerTest, SimpleBranch) {
  Label l1;
  __ mov(R0, ShifterOperand(2));
  __ Bind(&l1);
  __ mov(R1, ShifterOperand(1));
  __ b(&l1);
  Label l2;
  __ b(&l2);
  __ mov(R1, ShifterOperand(2));
  __ Bind(&l2);
  __ mov(R0, ShifterOperand(3));

  Label l3;
  __ mov(R0, ShifterOperand(2));
  __ Bind(&l3);
  __ mov(R1, ShifterOperand(1));
  __ b(&l3, EQ);

  Label l4;
  __ b(&l4, EQ);
  __ mov(R1, ShifterOperand(2));
  __ Bind(&l4);
  __ mov(R0, ShifterOperand(3));

  // 2 linked labels.
  Label l5;
  __ b(&l5);
  __ mov(R1, ShifterOperand(4));
  __ b(&l5);
  __ mov(R1, ShifterOperand(5));
  __ Bind(&l5);
  __ mov(R0, ShifterOperand(6));

  EmitAndCheck(&assembler, "SimpleBranch");
}

TEST_F(Thumb2AssemblerTest, LongBranch) {
  __ Force32Bit();
  // 32 bit branches.
  Label l1;
  __ mov(R0, ShifterOperand(2));
  __ Bind(&l1);
  __ mov(R1, ShifterOperand(1));
  __ b(&l1);

  Label l2;
  __ b(&l2);
  __ mov(R1, ShifterOperand(2));
  __ Bind(&l2);
  __ mov(R0, ShifterOperand(3));

  Label l3;
  __ mov(R0, ShifterOperand(2));
  __ Bind(&l3);
  __ mov(R1, ShifterOperand(1));
  __ b(&l3, EQ);

  Label l4;
  __ b(&l4, EQ);
  __ mov(R1, ShifterOperand(2));
  __ Bind(&l4);
  __ mov(R0, ShifterOperand(3));

  // 2 linked labels.
  Label l5;
  __ b(&l5);
  __ mov(R1, ShifterOperand(4));
  __ b(&l5);
  __ mov(R1, ShifterOperand(5));
  __ Bind(&l5);
  __ mov(R0, ShifterOperand(6));

  EmitAndCheck(&assembler, "LongBranch");
}

TEST_F(Thumb2AssemblerTest, LoadMultiple) {
  // 16 bit.
  __ ldm(DB_W, R4, (1 << R0 | 1 << R3));

  // 32 bit.
  __ ldm(DB_W, R4, (1 << LR | 1 << R11));
  __ ldm(DB, R4, (1 << LR | 1 << R11));

  // Single reg is converted to ldr
  __ ldm(DB_W, R4, (1 << R5));

  EmitAndCheck(&assembler, "LoadMultiple");
}

TEST_F(Thumb2AssemblerTest, StoreMultiple) {
  // 16 bit.
  __ stm(IA_W, R4, (1 << R0 | 1 << R3));

  // 32 bit.
  __ stm(IA_W, R4, (1 << LR | 1 << R11));
  __ stm(IA, R4, (1 << LR | 1 << R11));

  // Single reg is converted to str
  __ stm(IA_W, R4, (1 << R5));
  __ stm(IA, R4, (1 << R5));

  EmitAndCheck(&assembler, "StoreMultiple");
}

TEST_F(Thumb2AssemblerTest, MovWMovT) {
  // Always 32 bit.
  __ movw(R4, 0);
  __ movw(R4, 0x34);
  __ movw(R9, 0x34);
  __ movw(R3, 0x1234);
  __ movw(R9, 0xffff);

  // Always 32 bit.
  __ movt(R0, 0);
  __ movt(R0, 0x1234);
  __ movt(R1, 0xffff);

  EmitAndCheck(&assembler, "MovWMovT");
}

TEST_F(Thumb2AssemblerTest, SpecialAddSub) {
  __ add(R2, SP, ShifterOperand(0x50));   // 16 bit.
  __ add(SP, SP, ShifterOperand(0x50));   // 16 bit.
  __ add(R8, SP, ShifterOperand(0x50));   // 32 bit.

  __ add(R2, SP, ShifterOperand(0xf00));  // 32 bit due to imm size.
  __ add(SP, SP, ShifterOperand(0xf00));  // 32 bit due to imm size.
  __ add(SP, SP, ShifterOperand(0xffc));  // 32 bit due to imm size; encoding T4.

  __ sub(SP, SP, ShifterOperand(0x50));   // 16 bit
  __ sub(R0, SP, ShifterOperand(0x50));   // 32 bit
  __ sub(R8, SP, ShifterOperand(0x50));   // 32 bit.

  __ sub(SP, SP, ShifterOperand(0xf00));  // 32 bit due to imm size
  __ sub(SP, SP, ShifterOperand(0xffc));  // 32 bit due to imm size; encoding T4.

  EmitAndCheck(&assembler, "SpecialAddSub");
}

TEST_F(Thumb2AssemblerTest, LoadFromOffset) {
  __ LoadFromOffset(kLoadWord, R2, R4, 12);
  __ LoadFromOffset(kLoadWord, R2, R4, 0xfff);
  __ LoadFromOffset(kLoadWord, R2, R4, 0x1000);
  __ LoadFromOffset(kLoadWord, R2, R4, 0x1000a4);
  __ LoadFromOffset(kLoadWord, R2, R4, 0x101000);
  __ LoadFromOffset(kLoadWord, R4, R4, 0x101000);
  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 12);
  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0xfff);
  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0x1000);
  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0x1000a4);
  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0x101000);
  __ LoadFromOffset(kLoadUnsignedHalfword, R4, R4, 0x101000);
  __ LoadFromOffset(kLoadWordPair, R2, R4, 12);
  __ LoadFromOffset(kLoadWordPair, R2, R4, 0x3fc);
  __ LoadFromOffset(kLoadWordPair, R2, R4, 0x400);
  __ LoadFromOffset(kLoadWordPair, R2, R4, 0x400a4);
  __ LoadFromOffset(kLoadWordPair, R2, R4, 0x40400);
  __ LoadFromOffset(kLoadWordPair, R4, R4, 0x40400);

  __ LoadFromOffset(kLoadWord, R0, R12, 12);  // 32-bit because of R12.
  __ LoadFromOffset(kLoadWord, R2, R4, 0xa4 - 0x100000);

  __ LoadFromOffset(kLoadSignedByte, R2, R4, 12);
  __ LoadFromOffset(kLoadUnsignedByte, R2, R4, 12);
  __ LoadFromOffset(kLoadSignedHalfword, R2, R4, 12);

  EmitAndCheck(&assembler, "LoadFromOffset");
}

TEST_F(Thumb2AssemblerTest, StoreToOffset) {
  __ StoreToOffset(kStoreWord, R2, R4, 12);
  __ StoreToOffset(kStoreWord, R2, R4, 0xfff);
  __ StoreToOffset(kStoreWord, R2, R4, 0x1000);
  __ StoreToOffset(kStoreWord, R2, R4, 0x1000a4);
  __ StoreToOffset(kStoreWord, R2, R4, 0x101000);
  __ StoreToOffset(kStoreWord, R4, R4, 0x101000);
  __ StoreToOffset(kStoreHalfword, R2, R4, 12);
  __ StoreToOffset(kStoreHalfword, R2, R4, 0xfff);
  __ StoreToOffset(kStoreHalfword, R2, R4, 0x1000);
  __ StoreToOffset(kStoreHalfword, R2, R4, 0x1000a4);
  __ StoreToOffset(kStoreHalfword, R2, R4, 0x101000);
  __ StoreToOffset(kStoreHalfword, R4, R4, 0x101000);
  __ StoreToOffset(kStoreWordPair, R2, R4, 12);
  __ StoreToOffset(kStoreWordPair, R2, R4, 0x3fc);
  __ StoreToOffset(kStoreWordPair, R2, R4, 0x400);
  __ StoreToOffset(kStoreWordPair, R2, R4, 0x400a4);
  __ StoreToOffset(kStoreWordPair, R2, R4, 0x40400);
  __ StoreToOffset(kStoreWordPair, R4, R4, 0x40400);

  __ StoreToOffset(kStoreWord, R0, R12, 12);  // 32-bit because of R12.
  __ StoreToOffset(kStoreWord, R2, R4, 0xa4 - 0x100000);

  __ StoreToOffset(kStoreByte, R2, R4, 12);

  EmitAndCheck(&assembler, "StoreToOffset");
}

TEST_F(Thumb2AssemblerTest, IfThen) {
  __ it(EQ);
  __ mov(R1, ShifterOperand(1), EQ);

  __ it(EQ, kItThen);
  __ mov(R1, ShifterOperand(1), EQ);
  __ mov(R2, ShifterOperand(2), EQ);

  __ it(EQ, kItElse);
  __ mov(R1, ShifterOperand(1), EQ);
  __ mov(R2, ShifterOperand(2), NE);

  __ it(EQ, kItThen, kItElse);
  __ mov(R1, ShifterOperand(1), EQ);
  __ mov(R2, ShifterOperand(2), EQ);
  __ mov(R3, ShifterOperand(3), NE);

  __ it(EQ, kItElse, kItElse);
  __ mov(R1, ShifterOperand(1), EQ);
  __ mov(R2, ShifterOperand(2), NE);
  __ mov(R3, ShifterOperand(3), NE);

  __ it(EQ, kItThen, kItThen, kItElse);
  __ mov(R1, ShifterOperand(1), EQ);
  __ mov(R2, ShifterOperand(2), EQ);
  __ mov(R3, ShifterOperand(3), EQ);
  __ mov(R4, ShifterOperand(4), NE);

  EmitAndCheck(&assembler, "IfThen");
}

TEST_F(Thumb2AssemblerTest, CbzCbnz) {
  Label l1;
  __ cbz(R2, &l1);
  __ mov(R1, ShifterOperand(3));
  __ mov(R2, ShifterOperand(3));
  __ Bind(&l1);
  __ mov(R2, ShifterOperand(4));

  Label l2;
  __ cbnz(R2, &l2);
  __ mov(R8, ShifterOperand(3));
  __ mov(R2, ShifterOperand(3));
  __ Bind(&l2);
  __ mov(R2, ShifterOperand(4));

  EmitAndCheck(&assembler, "CbzCbnz");
}

TEST_F(Thumb2AssemblerTest, Multiply) {
  __ mul(R0, R1, R0);
  __ mul(R0, R1, R2);
  __ mul(R8, R9, R8);
  __ mul(R8, R9, R10);

  __ mla(R0, R1, R2, R3);
  __ mla(R8, R9, R8, R9);

  __ mls(R0, R1, R2, R3);
  __ mls(R8, R9, R8, R9);

  __ umull(R0, R1, R2, R3);
  __ umull(R8, R9, R10, R11);

  EmitAndCheck(&assembler, "Multiply");
}

TEST_F(Thumb2AssemblerTest, Divide) {
  __ sdiv(R0, R1, R2);
  __ sdiv(R8, R9, R10);

  __ udiv(R0, R1, R2);
  __ udiv(R8, R9, R10);

  EmitAndCheck(&assembler, "Divide");
}

TEST_F(Thumb2AssemblerTest, VMov) {
  __ vmovs(S1, 1.0);
  __ vmovd(D1, 1.0);

  __ vmovs(S1, S2);
  __ vmovd(D1, D2);

  EmitAndCheck(&assembler, "VMov");
}


TEST_F(Thumb2AssemblerTest, BasicFloatingPoint) {
  __ vadds(S0, S1, S2);
  __ vsubs(S0, S1, S2);
  __ vmuls(S0, S1, S2);
  __ vmlas(S0, S1, S2);
  __ vmlss(S0, S1, S2);
  __ vdivs(S0, S1, S2);
  __ vabss(S0, S1);
  __ vnegs(S0, S1);
  __ vsqrts(S0, S1);

  __ vaddd(D0, D1, D2);
  __ vsubd(D0, D1, D2);
  __ vmuld(D0, D1, D2);
  __ vmlad(D0, D1, D2);
  __ vmlsd(D0, D1, D2);
  __ vdivd(D0, D1, D2);
  __ vabsd(D0, D1);
  __ vnegd(D0, D1);
  __ vsqrtd(D0, D1);

  EmitAndCheck(&assembler, "BasicFloatingPoint");
}

TEST_F(Thumb2AssemblerTest, FloatingPointConversions) {
  __ vcvtsd(S2, D2);
  __ vcvtds(D2, S2);

  __ vcvtis(S1, S2);
  __ vcvtsi(S1, S2);

  __ vcvtid(S1, D2);
  __ vcvtdi(D1, S2);

  __ vcvtus(S1, S2);
  __ vcvtsu(S1, S2);

  __ vcvtud(S1, D2);
  __ vcvtdu(D1, S2);

  EmitAndCheck(&assembler, "FloatingPointConversions");
}

TEST_F(Thumb2AssemblerTest, FloatingPointComparisons) {
  __ vcmps(S0, S1);
  __ vcmpd(D0, D1);

  __ vcmpsz(S2);
  __ vcmpdz(D2);

  EmitAndCheck(&assembler, "FloatingPointComparisons");
}

TEST_F(Thumb2AssemblerTest, Calls) {
  __ blx(LR);
  __ bx(LR);

  EmitAndCheck(&assembler, "Calls");
}

TEST_F(Thumb2AssemblerTest, Breakpoint) {
  __ bkpt(0);

  EmitAndCheck(&assembler, "Breakpoint");
}

TEST_F(Thumb2AssemblerTest, StrR1) {
  __ str(R1, Address(SP, 68));
  __ str(R1, Address(SP, 1068));

  EmitAndCheck(&assembler, "StrR1");
}

TEST_F(Thumb2AssemblerTest, VPushPop) {
  __ vpushs(S2, 4);
  __ vpushd(D2, 4);

  __ vpops(S2, 4);
  __ vpopd(D2, 4);

  EmitAndCheck(&assembler, "VPushPop");
}

TEST_F(Thumb2AssemblerTest, Max16BitBranch) {
  Label l1;
  __ b(&l1);
  for (int i = 0 ; i < (1 << 11) ; i += 2) {
    __ mov(R3, ShifterOperand(i & 0xff));
  }
  __ Bind(&l1);
  __ mov(R1, ShifterOperand(R2));

  EmitAndCheck(&assembler, "Max16BitBranch");
}

TEST_F(Thumb2AssemblerTest, Branch32) {
  Label l1;
  __ b(&l1);
  for (int i = 0 ; i < (1 << 11) + 2 ; i += 2) {
    __ mov(R3, ShifterOperand(i & 0xff));
  }
  __ Bind(&l1);
  __ mov(R1, ShifterOperand(R2));

  EmitAndCheck(&assembler, "Branch32");
}

TEST_F(Thumb2AssemblerTest, CompareAndBranchMax) {
  Label l1;
  __ cbz(R4, &l1);
  for (int i = 0 ; i < (1 << 7) ; i += 2) {
    __ mov(R3, ShifterOperand(i & 0xff));
  }
  __ Bind(&l1);
  __ mov(R1, ShifterOperand(R2));

  EmitAndCheck(&assembler, "CompareAndBranchMax");
}

TEST_F(Thumb2AssemblerTest, CompareAndBranchRelocation16) {
  Label l1;
  __ cbz(R4, &l1);
  for (int i = 0 ; i < (1 << 7) + 2 ; i += 2) {
    __ mov(R3, ShifterOperand(i & 0xff));
  }
  __ Bind(&l1);
  __ mov(R1, ShifterOperand(R2));

  EmitAndCheck(&assembler, "CompareAndBranchRelocation16");
}

TEST_F(Thumb2AssemblerTest, CompareAndBranchRelocation32) {
  Label l1;
  __ cbz(R4, &l1);
  for (int i = 0 ; i < (1 << 11) + 2 ; i += 2) {
    __ mov(R3, ShifterOperand(i & 0xff));
  }
  __ Bind(&l1);
  __ mov(R1, ShifterOperand(R2));

  EmitAndCheck(&assembler, "CompareAndBranchRelocation32");
}

TEST_F(Thumb2AssemblerTest, MixedBranch32) {
  Label l1;
  Label l2;
  __ b(&l1);      // Forwards.
  __ Bind(&l2);

  // Space to force relocation.
  for (int i = 0 ; i < (1 << 11) + 2 ; i += 2) {
    __ mov(R3, ShifterOperand(i & 0xff));
  }
  __ b(&l2);      // Backwards.
  __ Bind(&l1);
  __ mov(R1, ShifterOperand(R2));

  EmitAndCheck(&assembler, "MixedBranch32");
}

TEST_F(Thumb2AssemblerTest, Shifts) {
  // 16 bit selected for CcDontCare.
  __ Lsl(R0, R1, 5);
  __ Lsr(R0, R1, 5);
  __ Asr(R0, R1, 5);

  __ Lsl(R0, R0, R1);
  __ Lsr(R0, R0, R1);
  __ Asr(R0, R0, R1);
  __ Ror(R0, R0, R1);

  // 16 bit with kCcSet.
  __ Lsls(R0, R1, 5);
  __ Lsrs(R0, R1, 5);
  __ Asrs(R0, R1, 5);

  __ Lsls(R0, R0, R1);
  __ Lsrs(R0, R0, R1);
  __ Asrs(R0, R0, R1);
  __ Rors(R0, R0, R1);

  // 32-bit with kCcKeep.
  __ Lsl(R0, R1, 5, AL, kCcKeep);
  __ Lsr(R0, R1, 5, AL, kCcKeep);
  __ Asr(R0, R1, 5, AL, kCcKeep);

  __ Lsl(R0, R0, R1, AL, kCcKeep);
  __ Lsr(R0, R0, R1, AL, kCcKeep);
  __ Asr(R0, R0, R1, AL, kCcKeep);
  __ Ror(R0, R0, R1, AL, kCcKeep);

  // 32-bit because ROR immediate doesn't have a 16-bit version like the other shifts.
  __ Ror(R0, R1, 5);
  __ Rors(R0, R1, 5);
  __ Ror(R0, R1, 5, AL, kCcKeep);

  // 32 bit due to high registers.
  __ Lsl(R8, R1, 5);
  __ Lsr(R0, R8, 5);
  __ Asr(R8, R1, 5);
  __ Ror(R0, R8, 5);

  // 32 bit due to different Rd and Rn.
  __ Lsl(R0, R1, R2);
  __ Lsr(R0, R1, R2);
  __ Asr(R0, R1, R2);
  __ Ror(R0, R1, R2);

  // 32 bit due to use of high registers.
  __ Lsl(R8, R1, R2);
  __ Lsr(R0, R8, R2);
  __ Asr(R0, R1, R8);

  // S bit (all 32 bit)

  // 32 bit due to high registers.
  __ Lsls(R8, R1, 5);
  __ Lsrs(R0, R8, 5);
  __ Asrs(R8, R1, 5);
  __ Rors(R0, R8, 5);

  // 32 bit due to different Rd and Rn.
  __ Lsls(R0, R1, R2);
  __ Lsrs(R0, R1, R2);
  __ Asrs(R0, R1, R2);
  __ Rors(R0, R1, R2);

  // 32 bit due to use of high registers.
  __ Lsls(R8, R1, R2);
  __ Lsrs(R0, R8, R2);
  __ Asrs(R0, R1, R8);

  EmitAndCheck(&assembler, "Shifts");
}

TEST_F(Thumb2AssemblerTest, LoadStoreRegOffset) {
  // 16 bit.
  __ ldr(R0, Address(R1, R2));
  __ str(R0, Address(R1, R2));

  // 32 bit due to shift.
  __ ldr(R0, Address(R1, R2, LSL, 1));
  __ str(R0, Address(R1, R2, LSL, 1));

  __ ldr(R0, Address(R1, R2, LSL, 3));
  __ str(R0, Address(R1, R2, LSL, 3));

  // 32 bit due to high register use.
  __ ldr(R8, Address(R1, R2));
  __ str(R8, Address(R1, R2));

  __ ldr(R1, Address(R8, R2));
  __ str(R2, Address(R8, R2));

  __ ldr(R0, Address(R1, R8));
  __ str(R0, Address(R1, R8));

  EmitAndCheck(&assembler, "LoadStoreRegOffset");
}

TEST_F(Thumb2AssemblerTest, LoadStoreLimits) {
  __ ldr(R0, Address(R4, 124));     // 16 bit.
  __ ldr(R0, Address(R4, 128));     // 32 bit.

  __ ldrb(R0, Address(R4, 31));     // 16 bit.
  __ ldrb(R0, Address(R4, 32));     // 32 bit.

  __ ldrh(R0, Address(R4, 62));     // 16 bit.
  __ ldrh(R0, Address(R4, 64));     // 32 bit.

  __ ldrsb(R0, Address(R4, 31));     // 32 bit.
  __ ldrsb(R0, Address(R4, 32));     // 32 bit.

  __ ldrsh(R0, Address(R4, 62));     // 32 bit.
  __ ldrsh(R0, Address(R4, 64));     // 32 bit.

  __ str(R0, Address(R4, 124));     // 16 bit.
  __ str(R0, Address(R4, 128));     // 32 bit.

  __ strb(R0, Address(R4, 31));     // 16 bit.
  __ strb(R0, Address(R4, 32));     // 32 bit.

  __ strh(R0, Address(R4, 62));     // 16 bit.
  __ strh(R0, Address(R4, 64));     // 32 bit.

  EmitAndCheck(&assembler, "LoadStoreLimits");
}

TEST_F(Thumb2AssemblerTest, CompareAndBranch) {
  Label label;
  __ CompareAndBranchIfZero(arm::R0, &label);
  __ CompareAndBranchIfZero(arm::R11, &label);
  __ CompareAndBranchIfNonZero(arm::R0, &label);
  __ CompareAndBranchIfNonZero(arm::R11, &label);
  __ Bind(&label);

  EmitAndCheck(&assembler, "CompareAndBranch");
}

TEST_F(Thumb2AssemblerTest, AddConstant) {
  // Low registers, Rd != Rn.
  __ AddConstant(R0, R1, 0);                          // MOV.
  __ AddConstant(R0, R1, 1);                          // 16-bit ADDS, encoding T1.
  __ AddConstant(R0, R1, 7);                          // 16-bit ADDS, encoding T1.
  __ AddConstant(R0, R1, 8);                          // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 255);                        // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 256);                        // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 257);                        // 32-bit ADD, encoding T4.
  __ AddConstant(R0, R1, 0xfff);                      // 32-bit ADD, encoding T4.
  __ AddConstant(R0, R1, 0x1000);                     // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 0x1001);                     // MVN+SUB.
  __ AddConstant(R0, R1, 0x1002);                     // MOVW+ADD.
  __ AddConstant(R0, R1, 0xffff);                     // MOVW+ADD.
  __ AddConstant(R0, R1, 0x10000);                    // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 0x10001);                    // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 0x10002);                    // MVN+SUB.
  __ AddConstant(R0, R1, 0x10003);                    // MOVW+MOVT+ADD.
  __ AddConstant(R0, R1, -1);                         // 16-bit SUBS.
  __ AddConstant(R0, R1, -7);                         // 16-bit SUBS.
  __ AddConstant(R0, R1, -8);                         // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -255);                       // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -256);                       // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -257);                       // 32-bit SUB, encoding T4.
  __ AddConstant(R0, R1, -0xfff);                     // 32-bit SUB, encoding T4.
  __ AddConstant(R0, R1, -0x1000);                    // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -0x1001);                    // MVN+ADD.
  __ AddConstant(R0, R1, -0x1002);                    // MOVW+SUB.
  __ AddConstant(R0, R1, -0xffff);                    // MOVW+SUB.
  __ AddConstant(R0, R1, -0x10000);                   // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -0x10001);                   // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -0x10002);                   // MVN+ADD.
  __ AddConstant(R0, R1, -0x10003);                   // MOVW+MOVT+ADD.

  // Low registers, Rd == Rn.
  __ AddConstant(R0, R0, 0);                          // Nothing.
  __ AddConstant(R1, R1, 1);                          // 16-bit ADDS, encoding T2,
  __ AddConstant(R0, R0, 7);                          // 16-bit ADDS, encoding T2.
  __ AddConstant(R1, R1, 8);                          // 16-bit ADDS, encoding T2.
  __ AddConstant(R0, R0, 255);                        // 16-bit ADDS, encoding T2.
  __ AddConstant(R1, R1, 256);                        // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R0, 257);                        // 32-bit ADD, encoding T4.
  __ AddConstant(R1, R1, 0xfff);                      // 32-bit ADD, encoding T4.
  __ AddConstant(R0, R0, 0x1000);                     // 32-bit ADD, encoding T3.
  __ AddConstant(R1, R1, 0x1001);                     // MVN+SUB.
  __ AddConstant(R0, R0, 0x1002);                     // MOVW+ADD.
  __ AddConstant(R1, R1, 0xffff);                     // MOVW+ADD.
  __ AddConstant(R0, R0, 0x10000);                    // 32-bit ADD, encoding T3.
  __ AddConstant(R1, R1, 0x10001);                    // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R0, 0x10002);                    // MVN+SUB.
  __ AddConstant(R1, R1, 0x10003);                    // MOVW+MOVT+ADD.
  __ AddConstant(R0, R0, -1);                         // 16-bit SUBS, encoding T2.
  __ AddConstant(R1, R1, -7);                         // 16-bit SUBS, encoding T2.
  __ AddConstant(R0, R0, -8);                         // 16-bit SUBS, encoding T2.
  __ AddConstant(R1, R1, -255);                       // 16-bit SUBS, encoding T2.
  __ AddConstant(R0, R0, -256);                       // 32-bit SUB, encoding T3.
  __ AddConstant(R1, R1, -257);                       // 32-bit SUB, encoding T4.
  __ AddConstant(R0, R0, -0xfff);                     // 32-bit SUB, encoding T4.
  __ AddConstant(R1, R1, -0x1000);                    // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R0, -0x1001);                    // MVN+ADD.
  __ AddConstant(R1, R1, -0x1002);                    // MOVW+SUB.
  __ AddConstant(R0, R0, -0xffff);                    // MOVW+SUB.
  __ AddConstant(R1, R1, -0x10000);                   // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R0, -0x10001);                   // 32-bit SUB, encoding T3.
  __ AddConstant(R1, R1, -0x10002);                   // MVN+ADD.
  __ AddConstant(R0, R0, -0x10003);                   // MOVW+MOVT+ADD.

  // High registers.
  __ AddConstant(R8, R8, 0);                          // Nothing.
  __ AddConstant(R8, R1, 1);                          // 32-bit ADD, encoding T3,
  __ AddConstant(R0, R8, 7);                          // 32-bit ADD, encoding T3.
  __ AddConstant(R8, R8, 8);                          // 32-bit ADD, encoding T3.
  __ AddConstant(R8, R1, 255);                        // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R8, 256);                        // 32-bit ADD, encoding T3.
  __ AddConstant(R8, R8, 257);                        // 32-bit ADD, encoding T4.
  __ AddConstant(R8, R1, 0xfff);                      // 32-bit ADD, encoding T4.
  __ AddConstant(R0, R8, 0x1000);                     // 32-bit ADD, encoding T3.
  __ AddConstant(R8, R8, 0x1001);                     // MVN+SUB.
  __ AddConstant(R0, R1, 0x1002);                     // MOVW+ADD.
  __ AddConstant(R0, R8, 0xffff);                     // MOVW+ADD.
  __ AddConstant(R8, R8, 0x10000);                    // 32-bit ADD, encoding T3.
  __ AddConstant(R8, R1, 0x10001);                    // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R8, 0x10002);                    // MVN+SUB.
  __ AddConstant(R0, R8, 0x10003);                    // MOVW+MOVT+ADD.
  __ AddConstant(R8, R8, -1);                         // 32-bit ADD, encoding T3.
  __ AddConstant(R8, R1, -7);                         // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R8, -8);                         // 32-bit SUB, encoding T3.
  __ AddConstant(R8, R8, -255);                       // 32-bit SUB, encoding T3.
  __ AddConstant(R8, R1, -256);                       // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R8, -257);                       // 32-bit SUB, encoding T4.
  __ AddConstant(R8, R8, -0xfff);                     // 32-bit SUB, encoding T4.
  __ AddConstant(R8, R1, -0x1000);                    // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R8, -0x1001);                    // MVN+ADD.
  __ AddConstant(R0, R1, -0x1002);                    // MOVW+SUB.
  __ AddConstant(R8, R1, -0xffff);                    // MOVW+SUB.
  __ AddConstant(R0, R8, -0x10000);                   // 32-bit SUB, encoding T3.
  __ AddConstant(R8, R8, -0x10001);                   // 32-bit SUB, encoding T3.
  __ AddConstant(R8, R1, -0x10002);                   // MVN+SUB.
  __ AddConstant(R0, R8, -0x10003);                   // MOVW+MOVT+ADD.

  // Low registers, Rd != Rn, kCcKeep.
  __ AddConstant(R0, R1, 0, AL, kCcKeep);             // MOV.
  __ AddConstant(R0, R1, 1, AL, kCcKeep);             // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 7, AL, kCcKeep);             // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 8, AL, kCcKeep);             // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 255, AL, kCcKeep);           // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 256, AL, kCcKeep);           // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 257, AL, kCcKeep);           // 32-bit ADD, encoding T4.
  __ AddConstant(R0, R1, 0xfff, AL, kCcKeep);         // 32-bit ADD, encoding T4.
  __ AddConstant(R0, R1, 0x1000, AL, kCcKeep);        // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 0x1001, AL, kCcKeep);        // MVN+SUB.
  __ AddConstant(R0, R1, 0x1002, AL, kCcKeep);        // MOVW+ADD.
  __ AddConstant(R0, R1, 0xffff, AL, kCcKeep);        // MOVW+ADD.
  __ AddConstant(R0, R1, 0x10000, AL, kCcKeep);       // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 0x10001, AL, kCcKeep);       // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, 0x10002, AL, kCcKeep);       // MVN+SUB.
  __ AddConstant(R0, R1, 0x10003, AL, kCcKeep);       // MOVW+MOVT+ADD.
  __ AddConstant(R0, R1, -1, AL, kCcKeep);            // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R1, -7, AL, kCcKeep);            // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -8, AL, kCcKeep);            // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -255, AL, kCcKeep);          // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -256, AL, kCcKeep);          // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -257, AL, kCcKeep);          // 32-bit SUB, encoding T4.
  __ AddConstant(R0, R1, -0xfff, AL, kCcKeep);        // 32-bit SUB, encoding T4.
  __ AddConstant(R0, R1, -0x1000, AL, kCcKeep);       // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -0x1001, AL, kCcKeep);       // MVN+ADD.
  __ AddConstant(R0, R1, -0x1002, AL, kCcKeep);       // MOVW+SUB.
  __ AddConstant(R0, R1, -0xffff, AL, kCcKeep);       // MOVW+SUB.
  __ AddConstant(R0, R1, -0x10000, AL, kCcKeep);      // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -0x10001, AL, kCcKeep);      // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R1, -0x10002, AL, kCcKeep);      // MVN+ADD.
  __ AddConstant(R0, R1, -0x10003, AL, kCcKeep);      // MOVW+MOVT+ADD.

  // Low registers, Rd == Rn, kCcKeep.
  __ AddConstant(R0, R0, 0, AL, kCcKeep);             // Nothing.
  __ AddConstant(R1, R1, 1, AL, kCcKeep);             // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R0, 7, AL, kCcKeep);             // 32-bit ADD, encoding T3.
  __ AddConstant(R1, R1, 8, AL, kCcKeep);             // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R0, 255, AL, kCcKeep);           // 32-bit ADD, encoding T3.
  __ AddConstant(R1, R1, 256, AL, kCcKeep);           // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R0, 257, AL, kCcKeep);           // 32-bit ADD, encoding T4.
  __ AddConstant(R1, R1, 0xfff, AL, kCcKeep);         // 32-bit ADD, encoding T4.
  __ AddConstant(R0, R0, 0x1000, AL, kCcKeep);        // 32-bit ADD, encoding T3.
  __ AddConstant(R1, R1, 0x1001, AL, kCcKeep);        // MVN+SUB.
  __ AddConstant(R0, R0, 0x1002, AL, kCcKeep);        // MOVW+ADD.
  __ AddConstant(R1, R1, 0xffff, AL, kCcKeep);        // MOVW+ADD.
  __ AddConstant(R0, R0, 0x10000, AL, kCcKeep);       // 32-bit ADD, encoding T3.
  __ AddConstant(R1, R1, 0x10001, AL, kCcKeep);       // 32-bit ADD, encoding T3.
  __ AddConstant(R0, R0, 0x10002, AL, kCcKeep);       // MVN+SUB.
  __ AddConstant(R1, R1, 0x10003, AL, kCcKeep);       // MOVW+MOVT+ADD.
  __ AddConstant(R0, R0, -1, AL, kCcKeep);            // 32-bit ADD, encoding T3.
  __ AddConstant(R1, R1, -7, AL, kCcKeep);            // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R0, -8, AL, kCcKeep);            // 32-bit SUB, encoding T3.
  __ AddConstant(R1, R1, -255, AL, kCcKeep);          // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R0, -256, AL, kCcKeep);          // 32-bit SUB, encoding T3.
  __ AddConstant(R1, R1, -257, AL, kCcKeep);          // 32-bit SUB, encoding T4.
  __ AddConstant(R0, R0, -0xfff, AL, kCcKeep);        // 32-bit SUB, encoding T4.
  __ AddConstant(R1, R1, -0x1000, AL, kCcKeep);       // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R0, -0x1001, AL, kCcKeep);       // MVN+ADD.
  __ AddConstant(R1, R1, -0x1002, AL, kCcKeep);       // MOVW+SUB.
  __ AddConstant(R0, R0, -0xffff, AL, kCcKeep);       // MOVW+SUB.
  __ AddConstant(R1, R1, -0x10000, AL, kCcKeep);      // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R0, -0x10001, AL, kCcKeep);      // 32-bit SUB, encoding T3.
  __ AddConstant(R1, R1, -0x10002, AL, kCcKeep);      // MVN+ADD.
  __ AddConstant(R0, R0, -0x10003, AL, kCcKeep);      // MOVW+MOVT+ADD.

  // Low registers, Rd != Rn, kCcSet.
  __ AddConstant(R0, R1, 0, AL, kCcSet);              // 16-bit ADDS.
  __ AddConstant(R0, R1, 1, AL, kCcSet);              // 16-bit ADDS.
  __ AddConstant(R0, R1, 7, AL, kCcSet);              // 16-bit ADDS.
  __ AddConstant(R0, R1, 8, AL, kCcSet);              // 32-bit ADDS, encoding T3.
  __ AddConstant(R0, R1, 255, AL, kCcSet);            // 32-bit ADDS, encoding T3.
  __ AddConstant(R0, R1, 256, AL, kCcSet);            // 32-bit ADDS, encoding T3.
  __ AddConstant(R0, R1, 257, AL, kCcSet);            // MVN+SUBS.
  __ AddConstant(R0, R1, 0xfff, AL, kCcSet);          // MOVW+ADDS.
  __ AddConstant(R0, R1, 0x1000, AL, kCcSet);         // 32-bit ADDS, encoding T3.
  __ AddConstant(R0, R1, 0x1001, AL, kCcSet);         // MVN+SUBS.
  __ AddConstant(R0, R1, 0x1002, AL, kCcSet);         // MOVW+ADDS.
  __ AddConstant(R0, R1, 0xffff, AL, kCcSet);         // MOVW+ADDS.
  __ AddConstant(R0, R1, 0x10000, AL, kCcSet);        // 32-bit ADDS, encoding T3.
  __ AddConstant(R0, R1, 0x10001, AL, kCcSet);        // 32-bit ADDS, encoding T3.
  __ AddConstant(R0, R1, 0x10002, AL, kCcSet);        // MVN+SUBS.
  __ AddConstant(R0, R1, 0x10003, AL, kCcSet);        // MOVW+MOVT+ADDS.
  __ AddConstant(R0, R1, -1, AL, kCcSet);             // 16-bit SUBS.
  __ AddConstant(R0, R1, -7, AL, kCcSet);             // 16-bit SUBS.
  __ AddConstant(R0, R1, -8, AL, kCcSet);             // 32-bit SUBS, encoding T3.
  __ AddConstant(R0, R1, -255, AL, kCcSet);           // 32-bit SUBS, encoding T3.
  __ AddConstant(R0, R1, -256, AL, kCcSet);           // 32-bit SUBS, encoding T3.
  __ AddConstant(R0, R1, -257, AL, kCcSet);           // MVN+ADDS.
  __ AddConstant(R0, R1, -0xfff, AL, kCcSet);         // MOVW+SUBS.
  __ AddConstant(R0, R1, -0x1000, AL, kCcSet);        // 32-bit SUBS, encoding T3.
  __ AddConstant(R0, R1, -0x1001, AL, kCcSet);        // MVN+ADDS.
  __ AddConstant(R0, R1, -0x1002, AL, kCcSet);        // MOVW+SUBS.
  __ AddConstant(R0, R1, -0xffff, AL, kCcSet);        // MOVW+SUBS.
  __ AddConstant(R0, R1, -0x10000, AL, kCcSet);       // 32-bit SUBS, encoding T3.
  __ AddConstant(R0, R1, -0x10001, AL, kCcSet);       // 32-bit SUBS, encoding T3.
  __ AddConstant(R0, R1, -0x10002, AL, kCcSet);       // MVN+ADDS.
  __ AddConstant(R0, R1, -0x10003, AL, kCcSet);       // MOVW+MOVT+ADDS.

  // Low registers, Rd == Rn, kCcSet.
  __ AddConstant(R0, R0, 0, AL, kCcSet);              // 16-bit ADDS, encoding T2.
  __ AddConstant(R1, R1, 1, AL, kCcSet);              // 16-bit ADDS, encoding T2.
  __ AddConstant(R0, R0, 7, AL, kCcSet);              // 16-bit ADDS, encoding T2.
  __ AddConstant(R1, R1, 8, AL, kCcSet);              // 16-bit ADDS, encoding T2.
  __ AddConstant(R0, R0, 255, AL, kCcSet);            // 16-bit ADDS, encoding T2.
  __ AddConstant(R1, R1, 256, AL, kCcSet);            // 32-bit ADDS, encoding T3.
  __ AddConstant(R0, R0, 257, AL, kCcSet);            // MVN+SUBS.
  __ AddConstant(R1, R1, 0xfff, AL, kCcSet);          // MOVW+ADDS.
  __ AddConstant(R0, R0, 0x1000, AL, kCcSet);         // 32-bit ADDS, encoding T3.
  __ AddConstant(R1, R1, 0x1001, AL, kCcSet);         // MVN+SUBS.
  __ AddConstant(R0, R0, 0x1002, AL, kCcSet);         // MOVW+ADDS.
  __ AddConstant(R1, R1, 0xffff, AL, kCcSet);         // MOVW+ADDS.
  __ AddConstant(R0, R0, 0x10000, AL, kCcSet);        // 32-bit ADDS, encoding T3.
  __ AddConstant(R1, R1, 0x10001, AL, kCcSet);        // 32-bit ADDS, encoding T3.
  __ AddConstant(R0, R0, 0x10002, AL, kCcSet);        // MVN+SUBS.
  __ AddConstant(R1, R1, 0x10003, AL, kCcSet);        // MOVW+MOVT+ADDS.
  __ AddConstant(R0, R0, -1, AL, kCcSet);             // 16-bit SUBS, encoding T2.
  __ AddConstant(R1, R1, -7, AL, kCcSet);             // 16-bit SUBS, encoding T2.
  __ AddConstant(R0, R0, -8, AL, kCcSet);             // 16-bit SUBS, encoding T2.
  __ AddConstant(R1, R1, -255, AL, kCcSet);           // 16-bit SUBS, encoding T2.
  __ AddConstant(R0, R0, -256, AL, kCcSet);           // 32-bit SUB, encoding T3.
  __ AddConstant(R1, R1, -257, AL, kCcSet);           // MNV+ADDS.
  __ AddConstant(R0, R0, -0xfff, AL, kCcSet);         // MOVW+SUBS.
  __ AddConstant(R1, R1, -0x1000, AL, kCcSet);        // 32-bit SUB, encoding T3.
  __ AddConstant(R0, R0, -0x1001, AL, kCcSet);        // MVN+ADDS.
  __ AddConstant(R1, R1, -0x1002, AL, kCcSet);        // MOVW+SUBS.
  __ AddConstant(R0, R0, -0xffff, AL, kCcSet);        // MOVW+SUBS.
  __ AddConstant(R1, R1, -0x10000, AL, kCcSet);       // 32-bit SUBS, encoding T3.
  __ AddConstant(R0, R0, -0x10001, AL, kCcSet);       // 32-bit SUBS, encoding T3.
  __ AddConstant(R1, R1, -0x10002, AL, kCcSet);       // MVN+ADDS.
  __ AddConstant(R0, R0, -0x10003, AL, kCcSet);       // MOVW+MOVT+ADDS.

  __ it(EQ);
  __ AddConstant(R0, R1, 1, EQ, kCcSet);              // 32-bit ADDS, encoding T3.
  __ it(NE);
  __ AddConstant(R0, R1, 1, NE, kCcKeep);             // 16-bit ADDS, encoding T1.
  __ it(GE);
  __ AddConstant(R0, R0, 1, GE, kCcSet);              // 32-bit ADDS, encoding T3.
  __ it(LE);
  __ AddConstant(R0, R0, 1, LE, kCcKeep);             // 16-bit ADDS, encoding T2.

  EmitAndCheck(&assembler, "AddConstant");
}

TEST_F(Thumb2AssemblerTest, CmpConstant) {
  __ CmpConstant(R0, 0);                              // 16-bit CMP.
  __ CmpConstant(R1, 1);                              // 16-bit CMP.
  __ CmpConstant(R0, 7);                              // 16-bit CMP.
  __ CmpConstant(R1, 8);                              // 16-bit CMP.
  __ CmpConstant(R0, 255);                            // 16-bit CMP.
  __ CmpConstant(R1, 256);                            // 32-bit CMP.
  __ CmpConstant(R0, 257);                            // MNV+CMN.
  __ CmpConstant(R1, 0xfff);                          // MOVW+CMP.
  __ CmpConstant(R0, 0x1000);                         // 32-bit CMP.
  __ CmpConstant(R1, 0x1001);                         // MNV+CMN.
  __ CmpConstant(R0, 0x1002);                         // MOVW+CMP.
  __ CmpConstant(R1, 0xffff);                         // MOVW+CMP.
  __ CmpConstant(R0, 0x10000);                        // 32-bit CMP.
  __ CmpConstant(R1, 0x10001);                        // 32-bit CMP.
  __ CmpConstant(R0, 0x10002);                        // MVN+CMN.
  __ CmpConstant(R1, 0x10003);                        // MOVW+MOVT+CMP.
  __ CmpConstant(R0, -1);                             // 32-bit CMP.
  __ CmpConstant(R1, -7);                             // CMN.
  __ CmpConstant(R0, -8);                             // CMN.
  __ CmpConstant(R1, -255);                           // CMN.
  __ CmpConstant(R0, -256);                           // CMN.
  __ CmpConstant(R1, -257);                           // MNV+CMP.
  __ CmpConstant(R0, -0xfff);                         // MOVW+CMN.
  __ CmpConstant(R1, -0x1000);                        // CMN.
  __ CmpConstant(R0, -0x1001);                        // MNV+CMP.
  __ CmpConstant(R1, -0x1002);                        // MOVW+CMN.
  __ CmpConstant(R0, -0xffff);                        // MOVW+CMN.
  __ CmpConstant(R1, -0x10000);                       // CMN.
  __ CmpConstant(R0, -0x10001);                       // CMN.
  __ CmpConstant(R1, -0x10002);                       // MVN+CMP.
  __ CmpConstant(R0, -0x10003);                       // MOVW+MOVT+CMP.

  __ CmpConstant(R8, 0);                              // 32-bit CMP.
  __ CmpConstant(R9, 1);                              // 32-bit CMP.
  __ CmpConstant(R8, 7);                              // 32-bit CMP.
  __ CmpConstant(R9, 8);                              // 32-bit CMP.
  __ CmpConstant(R8, 255);                            // 32-bit CMP.
  __ CmpConstant(R9, 256);                            // 32-bit CMP.
  __ CmpConstant(R8, 257);                            // MNV+CMN
  __ CmpConstant(R9, 0xfff);                          // MOVW+CMP.
  __ CmpConstant(R8, 0x1000);                         // 32-bit CMP.
  __ CmpConstant(R9, 0x1001);                         // MVN+CMN.
  __ CmpConstant(R8, 0x1002);                         // MOVW+CMP.
  __ CmpConstant(R9, 0xffff);                         // MOVW+CMP.
  __ CmpConstant(R8, 0x10000);                        // 32-bit CMP.
  __ CmpConstant(R9, 0x10001);                        // 32-bit CMP.
  __ CmpConstant(R8, 0x10002);                        // MVN+CMN.
  __ CmpConstant(R9, 0x10003);                        // MOVW+MOVT+CMP.
  __ CmpConstant(R8, -1);                             // 32-bit CMP
  __ CmpConstant(R9, -7);                             // CMN.
  __ CmpConstant(R8, -8);                             // CMN.
  __ CmpConstant(R9, -255);                           // CMN.
  __ CmpConstant(R8, -256);                           // CMN.
  __ CmpConstant(R9, -257);                           // MNV+CMP.
  __ CmpConstant(R8, -0xfff);                         // MOVW+CMN.
  __ CmpConstant(R9, -0x1000);                        // CMN.
  __ CmpConstant(R8, -0x1001);                        // MVN+CMP.
  __ CmpConstant(R9, -0x1002);                        // MOVW+CMN.
  __ CmpConstant(R8, -0xffff);                        // MOVW+CMN.
  __ CmpConstant(R9, -0x10000);                       // CMN.
  __ CmpConstant(R8, -0x10001);                       // CMN.
  __ CmpConstant(R9, -0x10002);                       // MVN+CMP.
  __ CmpConstant(R8, -0x10003);                       // MOVW+MOVT+CMP.

  EmitAndCheck(&assembler, "CmpConstant");
}

#define ENABLE_VIXL_TEST

#ifdef ENABLE_VIXL_TEST

#define ARM_VIXL

#ifdef ARM_VIXL
typedef arm::ArmVIXLJNIMacroAssembler JniAssemblerType;
#else
typedef arm::Thumb2Assembler AssemblerType;
#endif

class ArmVIXLAssemblerTest : public ::testing::Test {
 public:
  ArmVIXLAssemblerTest() : pool(), arena(&pool), assembler(&arena) { }

  ArenaPool pool;
  ArenaAllocator arena;
  JniAssemblerType assembler;
};

#undef __
#define __ assembler->

void EmitAndCheck(JniAssemblerType* assembler, const char* testname,
                  const char* const* results) {
  __ FinalizeCode();
  size_t cs = __ CodeSize();
  std::vector<uint8_t> managed_code(cs);
  MemoryRegion code(&managed_code[0], managed_code.size());
  __ FinalizeInstructions(code);

  DumpAndCheck(managed_code, testname, results);
}

void EmitAndCheck(JniAssemblerType* assembler, const char* testname) {
  InitResults();
  std::map<std::string, const char* const*>::iterator results = test_results.find(testname);
  ASSERT_NE(results, test_results.end());

  EmitAndCheck(assembler, testname, results->second);
}

#undef __
#define __ assembler.

TEST_F(ArmVIXLAssemblerTest, VixlJniHelpers) {
  const bool is_static = true;
  const bool is_synchronized = false;
  const bool is_critical_native = false;
  const char* shorty = "IIFII";

  ArenaPool pool;
  ArenaAllocator arena(&pool);

  std::unique_ptr<JniCallingConvention> jni_conv(
      JniCallingConvention::Create(&arena,
                                   is_static,
                                   is_synchronized,
                                   is_critical_native,
                                   shorty,
                                   kThumb2));
  std::unique_ptr<ManagedRuntimeCallingConvention> mr_conv(
      ManagedRuntimeCallingConvention::Create(&arena, is_static, is_synchronized, shorty, kThumb2));
  const int frame_size(jni_conv->FrameSize());
  ArrayRef<const ManagedRegister> callee_save_regs = jni_conv->CalleeSaveRegisters();

  const ManagedRegister method_register = ArmManagedRegister::FromCoreRegister(R0);
  const ManagedRegister scratch_register = ArmManagedRegister::FromCoreRegister(R12);

  __ BuildFrame(frame_size, mr_conv->MethodRegister(), callee_save_regs, mr_conv->EntrySpills());
  __ IncreaseFrameSize(32);

  // Loads
  __ IncreaseFrameSize(4096);
  __ Load(method_register, FrameOffset(32), 4);
  __ Load(method_register, FrameOffset(124), 4);
  __ Load(method_register, FrameOffset(132), 4);
  __ Load(method_register, FrameOffset(1020), 4);
  __ Load(method_register, FrameOffset(1024), 4);
  __ Load(scratch_register, FrameOffset(4092), 4);
  __ Load(scratch_register, FrameOffset(4096), 4);
  __ LoadRawPtrFromThread(scratch_register, ThreadOffset32(512));
  __ LoadRef(method_register, scratch_register, MemberOffset(128), /* unpoison_reference */ false);

  // Stores
  __ Store(FrameOffset(32), method_register, 4);
  __ Store(FrameOffset(124), method_register, 4);
  __ Store(FrameOffset(132), method_register, 4);
  __ Store(FrameOffset(1020), method_register, 4);
  __ Store(FrameOffset(1024), method_register, 4);
  __ Store(FrameOffset(4092), scratch_register, 4);
  __ Store(FrameOffset(4096), scratch_register, 4);
  __ StoreImmediateToFrame(FrameOffset(48), 0xFF, scratch_register);
  __ StoreImmediateToFrame(FrameOffset(48), 0xFFFFFF, scratch_register);
  __ StoreRawPtr(FrameOffset(48), scratch_register);
  __ StoreRef(FrameOffset(48), scratch_register);
  __ StoreSpanning(FrameOffset(48), method_register, FrameOffset(48), scratch_register);
  __ StoreStackOffsetToThread(ThreadOffset32(512), FrameOffset(4096), scratch_register);
  __ StoreStackPointerToThread(ThreadOffset32(512));

  // Other
  __ Call(method_register, FrameOffset(48), scratch_register);
  __ Copy(FrameOffset(48), FrameOffset(44), scratch_register, 4);
  __ CopyRawPtrFromThread(FrameOffset(44), ThreadOffset32(512), scratch_register);
  __ CopyRef(FrameOffset(48), FrameOffset(44), scratch_register);
  __ GetCurrentThread(method_register);
  __ GetCurrentThread(FrameOffset(48), scratch_register);
  __ Move(scratch_register, method_register, 4);
  __ VerifyObject(scratch_register, false);

  __ CreateHandleScopeEntry(scratch_register, FrameOffset(48), scratch_register, true);
  __ CreateHandleScopeEntry(scratch_register, FrameOffset(48), scratch_register, false);
  __ CreateHandleScopeEntry(method_register, FrameOffset(48), scratch_register, true);
  __ CreateHandleScopeEntry(FrameOffset(48), FrameOffset(64), scratch_register, true);
  __ CreateHandleScopeEntry(method_register, FrameOffset(0), scratch_register, true);
  __ CreateHandleScopeEntry(method_register, FrameOffset(1025), scratch_register, true);
  __ CreateHandleScopeEntry(scratch_register, FrameOffset(1025), scratch_register, true);

  __ ExceptionPoll(scratch_register, 0);

  __ DecreaseFrameSize(4096);
  __ DecreaseFrameSize(32);
  __ RemoveFrame(frame_size, callee_save_regs);

  EmitAndCheck(&assembler, "VixlJniHelpers");
}

#ifdef ARM_VIXL
#define R0 vixl::aarch32::r0
#define R2 vixl::aarch32::r2
#define R4 vixl::aarch32::r4
#define R12 vixl::aarch32::r12
#undef __
#define __ assembler.asm_.
#endif

TEST_F(ArmVIXLAssemblerTest, VixlLoadFromOffset) {
  __ LoadFromOffset(kLoadWord, R2, R4, 12);
  __ LoadFromOffset(kLoadWord, R2, R4, 0xfff);
  __ LoadFromOffset(kLoadWord, R2, R4, 0x1000);
  __ LoadFromOffset(kLoadWord, R2, R4, 0x1000a4);
  __ LoadFromOffset(kLoadWord, R2, R4, 0x101000);
  __ LoadFromOffset(kLoadWord, R4, R4, 0x101000);
  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 12);
  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0xfff);
  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0x1000);
  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0x1000a4);
  __ LoadFromOffset(kLoadUnsignedHalfword, R2, R4, 0x101000);
  __ LoadFromOffset(kLoadUnsignedHalfword, R4, R4, 0x101000);
  __ LoadFromOffset(kLoadWordPair, R2, R4, 12);
  __ LoadFromOffset(kLoadWordPair, R2, R4, 0x3fc);
  __ LoadFromOffset(kLoadWordPair, R2, R4, 0x400);
  __ LoadFromOffset(kLoadWordPair, R2, R4, 0x400a4);
  __ LoadFromOffset(kLoadWordPair, R2, R4, 0x40400);
  __ LoadFromOffset(kLoadWordPair, R4, R4, 0x40400);

  __ LoadFromOffset(kLoadWord, R0, R12, 12);  // 32-bit because of R12.
  __ LoadFromOffset(kLoadWord, R2, R4, 0xa4 - 0x100000);

  __ LoadFromOffset(kLoadSignedByte, R2, R4, 12);
  __ LoadFromOffset(kLoadUnsignedByte, R2, R4, 12);
  __ LoadFromOffset(kLoadSignedHalfword, R2, R4, 12);

  EmitAndCheck(&assembler, "VixlLoadFromOffset");
}

TEST_F(ArmVIXLAssemblerTest, VixlStoreToOffset) {
  __ StoreToOffset(kStoreWord, R2, R4, 12);
  __ StoreToOffset(kStoreWord, R2, R4, 0xfff);
  __ StoreToOffset(kStoreWord, R2, R4, 0x1000);
  __ StoreToOffset(kStoreWord, R2, R4, 0x1000a4);
  __ StoreToOffset(kStoreWord, R2, R4, 0x101000);
  __ StoreToOffset(kStoreWord, R4, R4, 0x101000);
  __ StoreToOffset(kStoreHalfword, R2, R4, 12);
  __ StoreToOffset(kStoreHalfword, R2, R4, 0xfff);
  __ StoreToOffset(kStoreHalfword, R2, R4, 0x1000);
  __ StoreToOffset(kStoreHalfword, R2, R4, 0x1000a4);
  __ StoreToOffset(kStoreHalfword, R2, R4, 0x101000);
  __ StoreToOffset(kStoreHalfword, R4, R4, 0x101000);
  __ StoreToOffset(kStoreWordPair, R2, R4, 12);
  __ StoreToOffset(kStoreWordPair, R2, R4, 0x3fc);
  __ StoreToOffset(kStoreWordPair, R2, R4, 0x400);
  __ StoreToOffset(kStoreWordPair, R2, R4, 0x400a4);
  __ StoreToOffset(kStoreWordPair, R2, R4, 0x40400);
  __ StoreToOffset(kStoreWordPair, R4, R4, 0x40400);

  __ StoreToOffset(kStoreWord, R0, R12, 12);  // 32-bit because of R12.
  __ StoreToOffset(kStoreWord, R2, R4, 0xa4 - 0x100000);

  __ StoreToOffset(kStoreByte, R2, R4, 12);

  EmitAndCheck(&assembler, "VixlStoreToOffset");
}

#undef __
#endif  // ENABLE_VIXL_TEST
}  // namespace arm
}  // namespace art
