//===- AArch64Disassembler.cpp - Disassembler for AArch64 ISA -------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the functions necessary to decode AArch64 instruction
// bitpatterns into MCInsts (with the help of TableGenerated information from
// the instruction definitions).
//
//===----------------------------------------------------------------------===//

/* Capstone Disassembly Engine */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2014 */

#ifdef CAPSTONE_HAS_ARM64

#include <stdio.h>	// DEBUG
#include <stdlib.h>

#include "../../cs_priv.h"
#include "../../utils.h"

#include "../../MCInst.h"
#include "../../MCInstrDesc.h"
#include "../../MCFixedLenDisassembler.h"
#include "../../MCRegisterInfo.h"
#include "../../MCDisassembler.h"

#include "AArch64BaseInfo.h"
#include "AArch64AddressingModes.h"


// Forward declare these because the autogenerated code will reference them.
// Definitions are further down.
static DecodeStatus DecodeFPR128RegisterClass(MCInst *Inst,
		unsigned RegNo, uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeFPR128_loRegisterClass(MCInst *Inst,
		unsigned RegNo,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeFPR64RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeFPR32RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeFPR16RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeFPR8RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeGPR64RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeGPR64spRegisterClass(MCInst *Inst,
		unsigned RegNo, uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeGPR32RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeGPR32spRegisterClass(MCInst *Inst,
		unsigned RegNo, uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeQQRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeQQQRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeQQQQRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeDDRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeDDDRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeDDDDRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address,
		const void *Decoder);

static DecodeStatus DecodeFixedPointScaleImm32(MCInst *Inst, unsigned Imm,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeFixedPointScaleImm64(MCInst *Inst, unsigned Imm,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodePCRelLabel19(MCInst *Inst, unsigned Imm,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeMemExtend(MCInst *Inst, unsigned Imm,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeMRSSystemRegister(MCInst *Inst, unsigned Imm,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeMSRSystemRegister(MCInst *Inst, unsigned Imm,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeThreeAddrSRegInstruction(MCInst *Inst,
		uint32_t insn,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeMoveImmInstruction(MCInst *Inst, uint32_t insn,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeUnsignedLdStInstruction(MCInst *Inst,
		uint32_t insn,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeSignedLdStInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeExclusiveLdStInstruction(MCInst *Inst,
		uint32_t insn,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodePairLdStInstruction(MCInst *Inst, uint32_t insn,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeAddSubERegInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeLogicalImmInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeModImmInstruction(MCInst *Inst, uint32_t insn,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeModImmTiedInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeAdrInstruction(MCInst *Inst, uint32_t insn,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeBaseAddSubImm(MCInst *Inst, uint32_t insn,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeUnconditionalBranch(MCInst *Inst, uint32_t insn,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeSystemPStateInstruction(MCInst *Inst,
		uint32_t insn,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeTestAndBranch(MCInst *Inst, uint32_t insn,
		uint64_t Address, const void *Decoder);

static DecodeStatus DecodeFMOVLaneInstruction(MCInst *Inst, unsigned Insn,
		uint64_t Address,
		const void *Decoder);
static DecodeStatus DecodeVecShiftR64Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeVecShiftR64ImmNarrow(MCInst *Inst, unsigned Imm,
		uint64_t Addr,
		const void *Decoder);
static DecodeStatus DecodeVecShiftR32Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeVecShiftR32ImmNarrow(MCInst *Inst, unsigned Imm,
		uint64_t Addr,
		const void *Decoder);
static DecodeStatus DecodeVecShiftR16Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeVecShiftR16ImmNarrow(MCInst *Inst, unsigned Imm,
		uint64_t Addr,
		const void *Decoder);
static DecodeStatus DecodeVecShiftR8Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeVecShiftL64Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeVecShiftL32Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeVecShiftL16Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeVecShiftL8Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder);

static bool Check(DecodeStatus *Out, DecodeStatus In)
{
	switch (In) {
		default:	// never reach
			return true;
		case MCDisassembler_Success:
			// Out stays the same.
			return true;
		case MCDisassembler_SoftFail:
			*Out = In;
			return true;
		case MCDisassembler_Fail:
			*Out = In;
			return false;
	}
	// llvm_unreachable("Invalid DecodeStatus!");
}

// Hacky: enable all features for disassembler
static uint64_t getFeatureBits(int feature)
{
	// enable all features
	return (uint64_t)-1;
}

#define GET_SUBTARGETINFO_ENUM
#include "AArch64GenSubtargetInfo.inc"

#include "AArch64GenDisassemblerTables.inc"

#define GET_INSTRINFO_ENUM
#include "AArch64GenInstrInfo.inc"

#define GET_REGINFO_ENUM
#define GET_REGINFO_MC_DESC
#include "AArch64GenRegisterInfo.inc"

#define Success MCDisassembler_Success
#define Fail MCDisassembler_Fail
#define SoftFail MCDisassembler_SoftFail

static DecodeStatus _getInstruction(cs_struct *ud, MCInst *MI,
		const uint8_t *code, size_t code_len,
		uint16_t *Size,
		uint64_t Address, MCRegisterInfo *MRI)
{
	uint32_t insn;
	DecodeStatus result;
	size_t i;

	if (code_len < 4) {
		// not enough data
		*Size = 0;
		return MCDisassembler_Fail;
	}

	if (MI->flat_insn->detail) {
		memset(MI->flat_insn->detail, 0, sizeof(cs_detail));
		for (i = 0; i < ARR_SIZE(MI->flat_insn->detail->arm64.operands); i++)
			MI->flat_insn->detail->arm64.operands[i].vector_index = -1;
	}

	if (MODE_IS_BIG_ENDIAN(ud->mode))
		insn = (code[3] << 0) | (code[2] << 8) |
			(code[1] <<  16) | (((uint32_t) code[0]) << 24);
	else
		insn = (((uint32_t) code[3]) << 24) | (code[2] << 16) |
			(code[1] <<  8) | (code[0] <<  0);

	// Calling the auto-generated decoder function.
	result = decodeInstruction(DecoderTable32, MI, insn, Address, MRI, 0);
	if (result != MCDisassembler_Fail) {
		*Size = 4;
		return result;
	}

	MCInst_clear(MI);
	*Size = 0;
	return MCDisassembler_Fail;
}

bool AArch64_getInstruction(csh ud, const uint8_t *code, size_t code_len,
		MCInst *instr, uint16_t *size, uint64_t address, void *info)
{
	DecodeStatus status = _getInstruction((cs_struct *)ud, instr,
			code, code_len,
			size,
			address, (MCRegisterInfo *)info);

	return status == MCDisassembler_Success;
}

static const unsigned FPR128DecoderTable[] = {
	AArch64_Q0,  AArch64_Q1,  AArch64_Q2,  AArch64_Q3,  AArch64_Q4,
	AArch64_Q5,  AArch64_Q6,  AArch64_Q7,  AArch64_Q8,  AArch64_Q9,
	AArch64_Q10, AArch64_Q11, AArch64_Q12, AArch64_Q13, AArch64_Q14,
	AArch64_Q15, AArch64_Q16, AArch64_Q17, AArch64_Q18, AArch64_Q19,
	AArch64_Q20, AArch64_Q21, AArch64_Q22, AArch64_Q23, AArch64_Q24,
	AArch64_Q25, AArch64_Q26, AArch64_Q27, AArch64_Q28, AArch64_Q29,
	AArch64_Q30, AArch64_Q31
};

static DecodeStatus DecodeFPR128RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Register;
	if (RegNo > 31)
		return Fail;

	Register = FPR128DecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static DecodeStatus DecodeFPR128_loRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	if (RegNo > 15)
		return Fail;

	return DecodeFPR128RegisterClass(Inst, RegNo, Addr, Decoder);
}

static const unsigned FPR64DecoderTable[] = {
	AArch64_D0,  AArch64_D1,  AArch64_D2,  AArch64_D3,  AArch64_D4,
	AArch64_D5,  AArch64_D6,  AArch64_D7,  AArch64_D8,  AArch64_D9,
	AArch64_D10, AArch64_D11, AArch64_D12, AArch64_D13, AArch64_D14,
	AArch64_D15, AArch64_D16, AArch64_D17, AArch64_D18, AArch64_D19,
	AArch64_D20, AArch64_D21, AArch64_D22, AArch64_D23, AArch64_D24,
	AArch64_D25, AArch64_D26, AArch64_D27, AArch64_D28, AArch64_D29,
	AArch64_D30, AArch64_D31
};

static DecodeStatus DecodeFPR64RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = FPR64DecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static const unsigned FPR32DecoderTable[] = {
	AArch64_S0,  AArch64_S1,  AArch64_S2,  AArch64_S3,  AArch64_S4,
	AArch64_S5,  AArch64_S6,  AArch64_S7,  AArch64_S8,  AArch64_S9,
	AArch64_S10, AArch64_S11, AArch64_S12, AArch64_S13, AArch64_S14,
	AArch64_S15, AArch64_S16, AArch64_S17, AArch64_S18, AArch64_S19,
	AArch64_S20, AArch64_S21, AArch64_S22, AArch64_S23, AArch64_S24,
	AArch64_S25, AArch64_S26, AArch64_S27, AArch64_S28, AArch64_S29,
	AArch64_S30, AArch64_S31
};

static DecodeStatus DecodeFPR32RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = FPR32DecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static const unsigned FPR16DecoderTable[] = {
	AArch64_H0,  AArch64_H1,  AArch64_H2,  AArch64_H3,  AArch64_H4,
	AArch64_H5,  AArch64_H6,  AArch64_H7,  AArch64_H8,  AArch64_H9,
	AArch64_H10, AArch64_H11, AArch64_H12, AArch64_H13, AArch64_H14,
	AArch64_H15, AArch64_H16, AArch64_H17, AArch64_H18, AArch64_H19,
	AArch64_H20, AArch64_H21, AArch64_H22, AArch64_H23, AArch64_H24,
	AArch64_H25, AArch64_H26, AArch64_H27, AArch64_H28, AArch64_H29,
	AArch64_H30, AArch64_H31
};

static DecodeStatus DecodeFPR16RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = FPR16DecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static const unsigned FPR8DecoderTable[] = {
	AArch64_B0,  AArch64_B1,  AArch64_B2,  AArch64_B3,  AArch64_B4,
	AArch64_B5,  AArch64_B6,  AArch64_B7,  AArch64_B8,  AArch64_B9,
	AArch64_B10, AArch64_B11, AArch64_B12, AArch64_B13, AArch64_B14,
	AArch64_B15, AArch64_B16, AArch64_B17, AArch64_B18, AArch64_B19,
	AArch64_B20, AArch64_B21, AArch64_B22, AArch64_B23, AArch64_B24,
	AArch64_B25, AArch64_B26, AArch64_B27, AArch64_B28, AArch64_B29,
	AArch64_B30, AArch64_B31
};

static DecodeStatus DecodeFPR8RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = FPR8DecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static const unsigned GPR64DecoderTable[] = {
	AArch64_X0,  AArch64_X1,  AArch64_X2,  AArch64_X3,  AArch64_X4,
	AArch64_X5,  AArch64_X6,  AArch64_X7,  AArch64_X8,  AArch64_X9,
	AArch64_X10, AArch64_X11, AArch64_X12, AArch64_X13, AArch64_X14,
	AArch64_X15, AArch64_X16, AArch64_X17, AArch64_X18, AArch64_X19,
	AArch64_X20, AArch64_X21, AArch64_X22, AArch64_X23, AArch64_X24,
	AArch64_X25, AArch64_X26, AArch64_X27, AArch64_X28, AArch64_FP,
	AArch64_LR,  AArch64_XZR
};

static DecodeStatus DecodeGPR64RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = GPR64DecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static DecodeStatus DecodeGPR64spRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = GPR64DecoderTable[RegNo];
	if (Register == AArch64_XZR)
		Register = AArch64_SP;

	MCOperand_CreateReg0(Inst, Register);

	return Success;
}

static const unsigned GPR32DecoderTable[] = {
	AArch64_W0,  AArch64_W1,  AArch64_W2,  AArch64_W3,  AArch64_W4,
	AArch64_W5,  AArch64_W6,  AArch64_W7,  AArch64_W8,  AArch64_W9,
	AArch64_W10, AArch64_W11, AArch64_W12, AArch64_W13, AArch64_W14,
	AArch64_W15, AArch64_W16, AArch64_W17, AArch64_W18, AArch64_W19,
	AArch64_W20, AArch64_W21, AArch64_W22, AArch64_W23, AArch64_W24,
	AArch64_W25, AArch64_W26, AArch64_W27, AArch64_W28, AArch64_W29,
	AArch64_W30, AArch64_WZR
};

static DecodeStatus DecodeGPR32RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = GPR32DecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static DecodeStatus DecodeGPR32spRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = GPR32DecoderTable[RegNo];
	if (Register == AArch64_WZR)
		Register = AArch64_WSP;

	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static const unsigned VectorDecoderTable[] = {
	AArch64_Q0,  AArch64_Q1,  AArch64_Q2,  AArch64_Q3,  AArch64_Q4,
	AArch64_Q5,  AArch64_Q6,  AArch64_Q7,  AArch64_Q8,  AArch64_Q9,
	AArch64_Q10, AArch64_Q11, AArch64_Q12, AArch64_Q13, AArch64_Q14,
	AArch64_Q15, AArch64_Q16, AArch64_Q17, AArch64_Q18, AArch64_Q19,
	AArch64_Q20, AArch64_Q21, AArch64_Q22, AArch64_Q23, AArch64_Q24,
	AArch64_Q25, AArch64_Q26, AArch64_Q27, AArch64_Q28, AArch64_Q29,
	AArch64_Q30, AArch64_Q31
};

static DecodeStatus DecodeVectorRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = VectorDecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static const unsigned QQDecoderTable[] = {
	AArch64_Q0_Q1,   AArch64_Q1_Q2,   AArch64_Q2_Q3,   AArch64_Q3_Q4,
	AArch64_Q4_Q5,   AArch64_Q5_Q6,   AArch64_Q6_Q7,   AArch64_Q7_Q8,
	AArch64_Q8_Q9,   AArch64_Q9_Q10,  AArch64_Q10_Q11, AArch64_Q11_Q12,
	AArch64_Q12_Q13, AArch64_Q13_Q14, AArch64_Q14_Q15, AArch64_Q15_Q16,
	AArch64_Q16_Q17, AArch64_Q17_Q18, AArch64_Q18_Q19, AArch64_Q19_Q20,
	AArch64_Q20_Q21, AArch64_Q21_Q22, AArch64_Q22_Q23, AArch64_Q23_Q24,
	AArch64_Q24_Q25, AArch64_Q25_Q26, AArch64_Q26_Q27, AArch64_Q27_Q28,
	AArch64_Q28_Q29, AArch64_Q29_Q30, AArch64_Q30_Q31, AArch64_Q31_Q0
};

static DecodeStatus DecodeQQRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr, const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = QQDecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static const unsigned QQQDecoderTable[] = {
	AArch64_Q0_Q1_Q2,    AArch64_Q1_Q2_Q3,    AArch64_Q2_Q3_Q4,
	AArch64_Q3_Q4_Q5,    AArch64_Q4_Q5_Q6,    AArch64_Q5_Q6_Q7,
	AArch64_Q6_Q7_Q8,    AArch64_Q7_Q8_Q9,    AArch64_Q8_Q9_Q10,
	AArch64_Q9_Q10_Q11,  AArch64_Q10_Q11_Q12, AArch64_Q11_Q12_Q13,
	AArch64_Q12_Q13_Q14, AArch64_Q13_Q14_Q15, AArch64_Q14_Q15_Q16,
	AArch64_Q15_Q16_Q17, AArch64_Q16_Q17_Q18, AArch64_Q17_Q18_Q19,
	AArch64_Q18_Q19_Q20, AArch64_Q19_Q20_Q21, AArch64_Q20_Q21_Q22,
	AArch64_Q21_Q22_Q23, AArch64_Q22_Q23_Q24, AArch64_Q23_Q24_Q25,
	AArch64_Q24_Q25_Q26, AArch64_Q25_Q26_Q27, AArch64_Q26_Q27_Q28,
	AArch64_Q27_Q28_Q29, AArch64_Q28_Q29_Q30, AArch64_Q29_Q30_Q31,
	AArch64_Q30_Q31_Q0,  AArch64_Q31_Q0_Q1
};

static DecodeStatus DecodeQQQRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr, const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = QQQDecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static const unsigned QQQQDecoderTable[] = {
	AArch64_Q0_Q1_Q2_Q3,     AArch64_Q1_Q2_Q3_Q4,     AArch64_Q2_Q3_Q4_Q5,
	AArch64_Q3_Q4_Q5_Q6,     AArch64_Q4_Q5_Q6_Q7,     AArch64_Q5_Q6_Q7_Q8,
	AArch64_Q6_Q7_Q8_Q9,     AArch64_Q7_Q8_Q9_Q10,    AArch64_Q8_Q9_Q10_Q11,
	AArch64_Q9_Q10_Q11_Q12,  AArch64_Q10_Q11_Q12_Q13, AArch64_Q11_Q12_Q13_Q14,
	AArch64_Q12_Q13_Q14_Q15, AArch64_Q13_Q14_Q15_Q16, AArch64_Q14_Q15_Q16_Q17,
	AArch64_Q15_Q16_Q17_Q18, AArch64_Q16_Q17_Q18_Q19, AArch64_Q17_Q18_Q19_Q20,
	AArch64_Q18_Q19_Q20_Q21, AArch64_Q19_Q20_Q21_Q22, AArch64_Q20_Q21_Q22_Q23,
	AArch64_Q21_Q22_Q23_Q24, AArch64_Q22_Q23_Q24_Q25, AArch64_Q23_Q24_Q25_Q26,
	AArch64_Q24_Q25_Q26_Q27, AArch64_Q25_Q26_Q27_Q28, AArch64_Q26_Q27_Q28_Q29,
	AArch64_Q27_Q28_Q29_Q30, AArch64_Q28_Q29_Q30_Q31, AArch64_Q29_Q30_Q31_Q0,
	AArch64_Q30_Q31_Q0_Q1,   AArch64_Q31_Q0_Q1_Q2
};

static DecodeStatus DecodeQQQQRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Register;
	if (RegNo > 31)
		return Fail;

	Register = QQQQDecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static const unsigned DDDecoderTable[] = {
	AArch64_D0_D1,   AArch64_D1_D2,   AArch64_D2_D3,   AArch64_D3_D4,
	AArch64_D4_D5,   AArch64_D5_D6,   AArch64_D6_D7,   AArch64_D7_D8,
	AArch64_D8_D9,   AArch64_D9_D10,  AArch64_D10_D11, AArch64_D11_D12,
	AArch64_D12_D13, AArch64_D13_D14, AArch64_D14_D15, AArch64_D15_D16,
	AArch64_D16_D17, AArch64_D17_D18, AArch64_D18_D19, AArch64_D19_D20,
	AArch64_D20_D21, AArch64_D21_D22, AArch64_D22_D23, AArch64_D23_D24,
	AArch64_D24_D25, AArch64_D25_D26, AArch64_D26_D27, AArch64_D27_D28,
	AArch64_D28_D29, AArch64_D29_D30, AArch64_D30_D31, AArch64_D31_D0
};

static DecodeStatus DecodeDDRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr, const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = DDDecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static const unsigned DDDDecoderTable[] = {
	AArch64_D0_D1_D2,    AArch64_D1_D2_D3,    AArch64_D2_D3_D4,
	AArch64_D3_D4_D5,    AArch64_D4_D5_D6,    AArch64_D5_D6_D7,
	AArch64_D6_D7_D8,    AArch64_D7_D8_D9,    AArch64_D8_D9_D10,
	AArch64_D9_D10_D11,  AArch64_D10_D11_D12, AArch64_D11_D12_D13,
	AArch64_D12_D13_D14, AArch64_D13_D14_D15, AArch64_D14_D15_D16,
	AArch64_D15_D16_D17, AArch64_D16_D17_D18, AArch64_D17_D18_D19,
	AArch64_D18_D19_D20, AArch64_D19_D20_D21, AArch64_D20_D21_D22,
	AArch64_D21_D22_D23, AArch64_D22_D23_D24, AArch64_D23_D24_D25,
	AArch64_D24_D25_D26, AArch64_D25_D26_D27, AArch64_D26_D27_D28,
	AArch64_D27_D28_D29, AArch64_D28_D29_D30, AArch64_D29_D30_D31,
	AArch64_D30_D31_D0,  AArch64_D31_D0_D1
};

static DecodeStatus DecodeDDDRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr, const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = DDDDecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static const unsigned DDDDDecoderTable[] = {
	AArch64_D0_D1_D2_D3,     AArch64_D1_D2_D3_D4,     AArch64_D2_D3_D4_D5,
	AArch64_D3_D4_D5_D6,     AArch64_D4_D5_D6_D7,     AArch64_D5_D6_D7_D8,
	AArch64_D6_D7_D8_D9,     AArch64_D7_D8_D9_D10,    AArch64_D8_D9_D10_D11,
	AArch64_D9_D10_D11_D12,  AArch64_D10_D11_D12_D13, AArch64_D11_D12_D13_D14,
	AArch64_D12_D13_D14_D15, AArch64_D13_D14_D15_D16, AArch64_D14_D15_D16_D17,
	AArch64_D15_D16_D17_D18, AArch64_D16_D17_D18_D19, AArch64_D17_D18_D19_D20,
	AArch64_D18_D19_D20_D21, AArch64_D19_D20_D21_D22, AArch64_D20_D21_D22_D23,
	AArch64_D21_D22_D23_D24, AArch64_D22_D23_D24_D25, AArch64_D23_D24_D25_D26,
	AArch64_D24_D25_D26_D27, AArch64_D25_D26_D27_D28, AArch64_D26_D27_D28_D29,
	AArch64_D27_D28_D29_D30, AArch64_D28_D29_D30_D31, AArch64_D29_D30_D31_D0,
	AArch64_D30_D31_D0_D1,   AArch64_D31_D0_D1_D2
};

static DecodeStatus DecodeDDDDRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Register;

	if (RegNo > 31)
		return Fail;

	Register = DDDDDecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);
	return Success;
}

static DecodeStatus DecodeFixedPointScaleImm32(MCInst *Inst, unsigned Imm,
		uint64_t Addr,
		const void *Decoder)
{
	// scale{5} is asserted as 1 in tblgen.
	Imm |= 0x20;  
	MCOperand_CreateImm0(Inst, 64 - Imm);
	return Success;
}

static DecodeStatus DecodeFixedPointScaleImm64(MCInst *Inst, unsigned Imm,
		uint64_t Addr,
		const void *Decoder)
{
	MCOperand_CreateImm0(Inst, 64 - Imm);
	return Success;
}

static DecodeStatus DecodePCRelLabel19(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder)
{
	int64_t ImmVal = Imm;

	// Sign-extend 19-bit immediate.
	if (ImmVal & (1 << (19 - 1)))
		ImmVal |= ~((1LL << 19) - 1);

	MCOperand_CreateImm0(Inst, ImmVal);
	return Success;
}

static DecodeStatus DecodeMemExtend(MCInst *Inst, unsigned Imm,
		uint64_t Address, const void *Decoder)
{
	MCOperand_CreateImm0(Inst, (Imm  >> 1) & 1);
	MCOperand_CreateImm0(Inst, Imm & 1);
	return Success;
}

static DecodeStatus DecodeMRSSystemRegister(MCInst *Inst, unsigned Imm,
		uint64_t Address, const void *Decoder)
{
	bool ValidNamed;
	char result[128];

	Imm |= 0x8000;
	MCOperand_CreateImm0(Inst, Imm);

	A64SysRegMapper_toString(&AArch64_MRSMapper, Imm, &ValidNamed, result);

	return ValidNamed ? Success : Fail;
}

static DecodeStatus DecodeMSRSystemRegister(MCInst *Inst, unsigned Imm,
		uint64_t Address,
		const void *Decoder)
{
	bool ValidNamed;
	char result[128];

	Imm |= 0x8000;
	MCOperand_CreateImm0(Inst, Imm);

	A64SysRegMapper_toString(&AArch64_MSRMapper, Imm, &ValidNamed, result);

	return ValidNamed ? Success : Fail;
}

static DecodeStatus DecodeFMOVLaneInstruction(MCInst *Inst, unsigned Insn,
		uint64_t Address,
		const void *Decoder)
{
	// This decoder exists to add the dummy Lane operand to the MCInst, which must
	// be 1 in assembly but has no other real manifestation.
	unsigned Rd = fieldFromInstruction(Insn, 0, 5);
	unsigned Rn = fieldFromInstruction(Insn, 5, 5);
	unsigned IsToVec = fieldFromInstruction(Insn, 16, 1);

	if (IsToVec) {
		DecodeFPR128RegisterClass(Inst, Rd, Address, Decoder);
		DecodeGPR64RegisterClass(Inst, Rn, Address, Decoder);
	} else {
		DecodeGPR64RegisterClass(Inst, Rd, Address, Decoder);
		DecodeFPR128RegisterClass(Inst, Rn, Address, Decoder);
	}

	// Add the lane
	MCOperand_CreateImm0(Inst, 1);

	return Success;
}

static DecodeStatus DecodeVecShiftRImm(MCInst *Inst, unsigned Imm,
		unsigned Add)
{
	MCOperand_CreateImm0(Inst, Add - Imm);
	return Success;
}

static DecodeStatus DecodeVecShiftLImm(MCInst *Inst, unsigned Imm,
		unsigned Add)
{
	MCOperand_CreateImm0(Inst, (Imm + Add) & (Add - 1));
	return Success;
}

static DecodeStatus DecodeVecShiftR64Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder)
{
	return DecodeVecShiftRImm(Inst, Imm, 64);
}

static DecodeStatus DecodeVecShiftR64ImmNarrow(MCInst *Inst, unsigned Imm,
		uint64_t Addr,
		const void *Decoder)
{
	return DecodeVecShiftRImm(Inst, Imm | 0x20, 64);
}

static DecodeStatus DecodeVecShiftR32Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder)
{
	return DecodeVecShiftRImm(Inst, Imm, 32);
}

static DecodeStatus DecodeVecShiftR32ImmNarrow(MCInst *Inst, unsigned Imm,
		uint64_t Addr,
		const void *Decoder)
{
	return DecodeVecShiftRImm(Inst, Imm | 0x10, 32);
}

static DecodeStatus DecodeVecShiftR16Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder)
{
	return DecodeVecShiftRImm(Inst, Imm, 16);
}

static DecodeStatus DecodeVecShiftR16ImmNarrow(MCInst *Inst, unsigned Imm,
		uint64_t Addr,
		const void *Decoder)
{
	return DecodeVecShiftRImm(Inst, Imm | 0x8, 16);
}

static DecodeStatus DecodeVecShiftR8Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder)
{
	return DecodeVecShiftRImm(Inst, Imm, 8);
}

static DecodeStatus DecodeVecShiftL64Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder)
{
	return DecodeVecShiftLImm(Inst, Imm, 64);
}

static DecodeStatus DecodeVecShiftL32Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder)
{
	return DecodeVecShiftLImm(Inst, Imm, 32);
}

static DecodeStatus DecodeVecShiftL16Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder)
{
	return DecodeVecShiftLImm(Inst, Imm, 16);
}

static DecodeStatus DecodeVecShiftL8Imm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder)
{
	return DecodeVecShiftLImm(Inst, Imm, 8);
}

static DecodeStatus DecodeThreeAddrSRegInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Addr,
		const void *Decoder)
{
	unsigned Rd = fieldFromInstruction(insn, 0, 5);
	unsigned Rn = fieldFromInstruction(insn, 5, 5);
	unsigned Rm = fieldFromInstruction(insn, 16, 5);
	unsigned shiftHi = fieldFromInstruction(insn, 22, 2);
	unsigned shiftLo = fieldFromInstruction(insn, 10, 6);
	unsigned shift = (shiftHi << 6) | shiftLo;

	switch (MCInst_getOpcode(Inst)) {
		default:
			return Fail;
		case AArch64_ADDWrs:
		case AArch64_ADDSWrs:
		case AArch64_SUBWrs:
		case AArch64_SUBSWrs:
			// if shift == '11' then ReservedValue()
			if (shiftHi == 0x3)
				return Fail;
			// Deliberate fallthrough
		case AArch64_ANDWrs:
		case AArch64_ANDSWrs:
		case AArch64_BICWrs:
		case AArch64_BICSWrs:
		case AArch64_ORRWrs:
		case AArch64_ORNWrs:
		case AArch64_EORWrs:
		case AArch64_EONWrs: {
				// if sf == '0' and imm6<5> == '1' then ReservedValue()
				if (shiftLo >> 5 == 1)
					return Fail;
				DecodeGPR32RegisterClass(Inst, Rd, Addr, Decoder);
				DecodeGPR32RegisterClass(Inst, Rn, Addr, Decoder);
				DecodeGPR32RegisterClass(Inst, Rm, Addr, Decoder);
				break;
			}
		case AArch64_ADDXrs:
		case AArch64_ADDSXrs:
		case AArch64_SUBXrs:
		case AArch64_SUBSXrs:
				 // if shift == '11' then ReservedValue()
				 if (shiftHi == 0x3)
					 return Fail;
				 // Deliberate fallthrough
		case AArch64_ANDXrs:
		case AArch64_ANDSXrs:
		case AArch64_BICXrs:
		case AArch64_BICSXrs:
		case AArch64_ORRXrs:
		case AArch64_ORNXrs:
		case AArch64_EORXrs:
		case AArch64_EONXrs:
				 DecodeGPR64RegisterClass(Inst, Rd, Addr, Decoder);
				 DecodeGPR64RegisterClass(Inst, Rn, Addr, Decoder);
				 DecodeGPR64RegisterClass(Inst, Rm, Addr, Decoder);
				 break;
	}

	MCOperand_CreateImm0(Inst, shift);
	return Success;
}

static DecodeStatus DecodeMoveImmInstruction(MCInst *Inst, uint32_t insn,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Rd = fieldFromInstruction(insn, 0, 5);
	unsigned imm = fieldFromInstruction(insn, 5, 16);
	unsigned shift = fieldFromInstruction(insn, 21, 2);

	shift <<= 4;

	switch (MCInst_getOpcode(Inst)) {
		default:
			return Fail;
		case AArch64_MOVZWi:
		case AArch64_MOVNWi:
		case AArch64_MOVKWi:
			if (shift & (1U << 5))
				return Fail;
			DecodeGPR32RegisterClass(Inst, Rd, Addr, Decoder);
			break;
		case AArch64_MOVZXi:
		case AArch64_MOVNXi:
		case AArch64_MOVKXi:
			DecodeGPR64RegisterClass(Inst, Rd, Addr, Decoder);
			break;
	}

	if (MCInst_getOpcode(Inst) == AArch64_MOVKWi ||
			MCInst_getOpcode(Inst) == AArch64_MOVKXi)
		MCInst_addOperand2(Inst, MCInst_getOperand(Inst, 0));

	MCOperand_CreateImm0(Inst, imm);
	MCOperand_CreateImm0(Inst, shift);
	return Success;
}

static DecodeStatus DecodeUnsignedLdStInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Addr,
		const void *Decoder)
{
	unsigned Rt = fieldFromInstruction(insn, 0, 5);
	unsigned Rn = fieldFromInstruction(insn, 5, 5);
	unsigned offset = fieldFromInstruction(insn, 10, 12);

	switch (MCInst_getOpcode(Inst)) {
		default:
			return Fail;
		case AArch64_PRFMui:
			// Rt is an immediate in prefetch.
			MCOperand_CreateImm0(Inst, Rt);
			break;
		case AArch64_STRBBui:
		case AArch64_LDRBBui:
		case AArch64_LDRSBWui:
		case AArch64_STRHHui:
		case AArch64_LDRHHui:
		case AArch64_LDRSHWui:
		case AArch64_STRWui:
		case AArch64_LDRWui:
			DecodeGPR32RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_LDRSBXui:
		case AArch64_LDRSHXui:
		case AArch64_LDRSWui:
		case AArch64_STRXui:
		case AArch64_LDRXui:
			DecodeGPR64RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_LDRQui:
		case AArch64_STRQui:
			DecodeFPR128RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_LDRDui:
		case AArch64_STRDui:
			DecodeFPR64RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_LDRSui:
		case AArch64_STRSui:
			DecodeFPR32RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_LDRHui:
		case AArch64_STRHui:
			DecodeFPR16RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_LDRBui:
		case AArch64_STRBui:
			DecodeFPR8RegisterClass(Inst, Rt, Addr, Decoder);
			break;
	}

	DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder);
	//if (!Dis->tryAddingSymbolicOperand(Inst, offset, Addr, Fail, 0, 4))
	MCOperand_CreateImm0(Inst, offset);

	return Success;
}

static DecodeStatus DecodeSignedLdStInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Addr,
		const void *Decoder)
{
	bool IsLoad;
	bool IsIndexed;
	bool IsFP;
	unsigned Rt = fieldFromInstruction(insn, 0, 5);
	unsigned Rn = fieldFromInstruction(insn, 5, 5);
	int64_t offset = fieldFromInstruction(insn, 12, 9);

	// offset is a 9-bit signed immediate, so sign extend it to
	// fill the unsigned.
	if (offset & (1 << (9 - 1)))
		offset |= ~((1LL << 9) - 1);

	// First operand is always the writeback to the address register, if needed.
	switch (MCInst_getOpcode(Inst)) {
		default:
			break;
		case AArch64_LDRSBWpre:
		case AArch64_LDRSHWpre:
		case AArch64_STRBBpre:
		case AArch64_LDRBBpre:
		case AArch64_STRHHpre:
		case AArch64_LDRHHpre:
		case AArch64_STRWpre:
		case AArch64_LDRWpre:
		case AArch64_LDRSBWpost:
		case AArch64_LDRSHWpost:
		case AArch64_STRBBpost:
		case AArch64_LDRBBpost:
		case AArch64_STRHHpost:
		case AArch64_LDRHHpost:
		case AArch64_STRWpost:
		case AArch64_LDRWpost:
		case AArch64_LDRSBXpre:
		case AArch64_LDRSHXpre:
		case AArch64_STRXpre:
		case AArch64_LDRSWpre:
		case AArch64_LDRXpre:
		case AArch64_LDRSBXpost:
		case AArch64_LDRSHXpost:
		case AArch64_STRXpost:
		case AArch64_LDRSWpost:
		case AArch64_LDRXpost:
		case AArch64_LDRQpre:
		case AArch64_STRQpre:
		case AArch64_LDRQpost:
		case AArch64_STRQpost:
		case AArch64_LDRDpre:
		case AArch64_STRDpre:
		case AArch64_LDRDpost:
		case AArch64_STRDpost:
		case AArch64_LDRSpre:
		case AArch64_STRSpre:
		case AArch64_LDRSpost:
		case AArch64_STRSpost:
		case AArch64_LDRHpre:
		case AArch64_STRHpre:
		case AArch64_LDRHpost:
		case AArch64_STRHpost:
		case AArch64_LDRBpre:
		case AArch64_STRBpre:
		case AArch64_LDRBpost:
		case AArch64_STRBpost:
			DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder);
			break;
	}

	switch (MCInst_getOpcode(Inst)) {
		default:
			return Fail;
		case AArch64_PRFUMi:
			// Rt is an immediate in prefetch.
			MCOperand_CreateImm0(Inst, Rt);
			break;
		case AArch64_STURBBi:
		case AArch64_LDURBBi:
		case AArch64_LDURSBWi:
		case AArch64_STURHHi:
		case AArch64_LDURHHi:
		case AArch64_LDURSHWi:
		case AArch64_STURWi:
		case AArch64_LDURWi:
		case AArch64_LDTRSBWi:
		case AArch64_LDTRSHWi:
		case AArch64_STTRWi:
		case AArch64_LDTRWi:
		case AArch64_STTRHi:
		case AArch64_LDTRHi:
		case AArch64_LDTRBi:
		case AArch64_STTRBi:
		case AArch64_LDRSBWpre:
		case AArch64_LDRSHWpre:
		case AArch64_STRBBpre:
		case AArch64_LDRBBpre:
		case AArch64_STRHHpre:
		case AArch64_LDRHHpre:
		case AArch64_STRWpre:
		case AArch64_LDRWpre:
		case AArch64_LDRSBWpost:
		case AArch64_LDRSHWpost:
		case AArch64_STRBBpost:
		case AArch64_LDRBBpost:
		case AArch64_STRHHpost:
		case AArch64_LDRHHpost:
		case AArch64_STRWpost:
		case AArch64_LDRWpost:
			DecodeGPR32RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_LDURSBXi:
		case AArch64_LDURSHXi:
		case AArch64_LDURSWi:
		case AArch64_STURXi:
		case AArch64_LDURXi:
		case AArch64_LDTRSBXi:
		case AArch64_LDTRSHXi:
		case AArch64_LDTRSWi:
		case AArch64_STTRXi:
		case AArch64_LDTRXi:
		case AArch64_LDRSBXpre:
		case AArch64_LDRSHXpre:
		case AArch64_STRXpre:
		case AArch64_LDRSWpre:
		case AArch64_LDRXpre:
		case AArch64_LDRSBXpost:
		case AArch64_LDRSHXpost:
		case AArch64_STRXpost:
		case AArch64_LDRSWpost:
		case AArch64_LDRXpost:
			DecodeGPR64RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_LDURQi:
		case AArch64_STURQi:
		case AArch64_LDRQpre:
		case AArch64_STRQpre:
		case AArch64_LDRQpost:
		case AArch64_STRQpost:
			DecodeFPR128RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_LDURDi:
		case AArch64_STURDi:
		case AArch64_LDRDpre:
		case AArch64_STRDpre:
		case AArch64_LDRDpost:
		case AArch64_STRDpost:
			DecodeFPR64RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_LDURSi:
		case AArch64_STURSi:
		case AArch64_LDRSpre:
		case AArch64_STRSpre:
		case AArch64_LDRSpost:
		case AArch64_STRSpost:
			DecodeFPR32RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_LDURHi:
		case AArch64_STURHi:
		case AArch64_LDRHpre:
		case AArch64_STRHpre:
		case AArch64_LDRHpost:
		case AArch64_STRHpost:
			DecodeFPR16RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_LDURBi:
		case AArch64_STURBi:
		case AArch64_LDRBpre:
		case AArch64_STRBpre:
		case AArch64_LDRBpost:
		case AArch64_STRBpost:
			DecodeFPR8RegisterClass(Inst, Rt, Addr, Decoder);
			break;
	}

	DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder);
	MCOperand_CreateImm0(Inst, offset);

	IsLoad = fieldFromInstruction(insn, 22, 1) != 0;
	IsIndexed = fieldFromInstruction(insn, 10, 2) != 0;
	IsFP = fieldFromInstruction(insn, 26, 1) != 0;

	// Cannot write back to a transfer register (but xzr != sp).
	if (IsLoad && IsIndexed && !IsFP && Rn != 31 && Rt == Rn)
		return SoftFail;

	return Success;
}

static DecodeStatus DecodeExclusiveLdStInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Addr,
		const void *Decoder)
{
	unsigned Rt = fieldFromInstruction(insn, 0, 5);
	unsigned Rn = fieldFromInstruction(insn, 5, 5);
	unsigned Rt2 = fieldFromInstruction(insn, 10, 5);
	unsigned Rs = fieldFromInstruction(insn, 16, 5);
	unsigned Opcode = MCInst_getOpcode(Inst);

	switch (Opcode) {
		default:
			return Fail;
		case AArch64_STLXRW:
		case AArch64_STLXRB:
		case AArch64_STLXRH:
		case AArch64_STXRW:
		case AArch64_STXRB:
		case AArch64_STXRH:
			DecodeGPR32RegisterClass(Inst, Rs, Addr, Decoder);
			// FALLTHROUGH
		case AArch64_LDARW:
		case AArch64_LDARB:
		case AArch64_LDARH:
		case AArch64_LDAXRW:
		case AArch64_LDAXRB:
		case AArch64_LDAXRH:
		case AArch64_LDXRW:
		case AArch64_LDXRB:
		case AArch64_LDXRH:
		case AArch64_STLRW:
		case AArch64_STLRB:
		case AArch64_STLRH:
			DecodeGPR32RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_STLXRX:
		case AArch64_STXRX:
			DecodeGPR32RegisterClass(Inst, Rs, Addr, Decoder);
			// FALLTHROUGH
		case AArch64_LDARX:
		case AArch64_LDAXRX:
		case AArch64_LDXRX:
		case AArch64_STLRX:
			DecodeGPR64RegisterClass(Inst, Rt, Addr, Decoder);
			break;
		case AArch64_STLXPW:
		case AArch64_STXPW:
			DecodeGPR32RegisterClass(Inst, Rs, Addr, Decoder);
			// FALLTHROUGH
		case AArch64_LDAXPW:
		case AArch64_LDXPW:
			DecodeGPR32RegisterClass(Inst, Rt, Addr, Decoder);
			DecodeGPR32RegisterClass(Inst, Rt2, Addr, Decoder);
			break;
		case AArch64_STLXPX:
		case AArch64_STXPX:
			DecodeGPR32RegisterClass(Inst, Rs, Addr, Decoder);
			// FALLTHROUGH
		case AArch64_LDAXPX:
		case AArch64_LDXPX:
			DecodeGPR64RegisterClass(Inst, Rt, Addr, Decoder);
			DecodeGPR64RegisterClass(Inst, Rt2, Addr, Decoder);
			break;
	}

	DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder);

	// You shouldn't load to the same register twice in an instruction...
	if ((Opcode == AArch64_LDAXPW || Opcode == AArch64_LDXPW ||
				Opcode == AArch64_LDAXPX || Opcode == AArch64_LDXPX) &&
			Rt == Rt2)
		return SoftFail;

	return Success;
}

static DecodeStatus DecodePairLdStInstruction(MCInst *Inst, uint32_t insn,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Rt = fieldFromInstruction(insn, 0, 5);
	unsigned Rn = fieldFromInstruction(insn, 5, 5);
	unsigned Rt2 = fieldFromInstruction(insn, 10, 5);
	int64_t offset = fieldFromInstruction(insn, 15, 7);
	bool IsLoad = fieldFromInstruction(insn, 22, 1) != 0;
	unsigned Opcode = MCInst_getOpcode(Inst);
	bool NeedsDisjointWritebackTransfer = false;

	// offset is a 7-bit signed immediate, so sign extend it to
	// fill the unsigned.
	if (offset & (1 << (7 - 1)))
		offset |= ~((1LL << 7) - 1);

	// First operand is always writeback of base register.
	switch (Opcode) {
		default:
			break;
		case AArch64_LDPXpost:
		case AArch64_STPXpost:
		case AArch64_LDPSWpost:
		case AArch64_LDPXpre:
		case AArch64_STPXpre:
		case AArch64_LDPSWpre:
		case AArch64_LDPWpost:
		case AArch64_STPWpost:
		case AArch64_LDPWpre:
		case AArch64_STPWpre:
		case AArch64_LDPQpost:
		case AArch64_STPQpost:
		case AArch64_LDPQpre:
		case AArch64_STPQpre:
		case AArch64_LDPDpost:
		case AArch64_STPDpost:
		case AArch64_LDPDpre:
		case AArch64_STPDpre:
		case AArch64_LDPSpost:
		case AArch64_STPSpost:
		case AArch64_LDPSpre:
		case AArch64_STPSpre:
			DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder);
			break;
	}

	switch (Opcode) {
		default:
			return Fail;
		case AArch64_LDPXpost:
		case AArch64_STPXpost:
		case AArch64_LDPSWpost:
		case AArch64_LDPXpre:
		case AArch64_STPXpre:
		case AArch64_LDPSWpre:
			NeedsDisjointWritebackTransfer = true;
			// Fallthrough
		case AArch64_LDNPXi:
		case AArch64_STNPXi:
		case AArch64_LDPXi:
		case AArch64_STPXi:
		case AArch64_LDPSWi:
			DecodeGPR64RegisterClass(Inst, Rt, Addr, Decoder);
			DecodeGPR64RegisterClass(Inst, Rt2, Addr, Decoder);
			break;
		case AArch64_LDPWpost:
		case AArch64_STPWpost:
		case AArch64_LDPWpre:
		case AArch64_STPWpre:
			NeedsDisjointWritebackTransfer = true;
			// Fallthrough
		case AArch64_LDNPWi:
		case AArch64_STNPWi:
		case AArch64_LDPWi:
		case AArch64_STPWi:
			DecodeGPR32RegisterClass(Inst, Rt, Addr, Decoder);
			DecodeGPR32RegisterClass(Inst, Rt2, Addr, Decoder);
			break;
		case AArch64_LDNPQi:
		case AArch64_STNPQi:
		case AArch64_LDPQpost:
		case AArch64_STPQpost:
		case AArch64_LDPQi:
		case AArch64_STPQi:
		case AArch64_LDPQpre:
		case AArch64_STPQpre:
			DecodeFPR128RegisterClass(Inst, Rt, Addr, Decoder);
			DecodeFPR128RegisterClass(Inst, Rt2, Addr, Decoder);
			break;
		case AArch64_LDNPDi:
		case AArch64_STNPDi:
		case AArch64_LDPDpost:
		case AArch64_STPDpost:
		case AArch64_LDPDi:
		case AArch64_STPDi:
		case AArch64_LDPDpre:
		case AArch64_STPDpre:
			DecodeFPR64RegisterClass(Inst, Rt, Addr, Decoder);
			DecodeFPR64RegisterClass(Inst, Rt2, Addr, Decoder);
			break;
		case AArch64_LDNPSi:
		case AArch64_STNPSi:
		case AArch64_LDPSpost:
		case AArch64_STPSpost:
		case AArch64_LDPSi:
		case AArch64_STPSi:
		case AArch64_LDPSpre:
		case AArch64_STPSpre:
			DecodeFPR32RegisterClass(Inst, Rt, Addr, Decoder);
			DecodeFPR32RegisterClass(Inst, Rt2, Addr, Decoder);
			break;
	}

	DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder);
	MCOperand_CreateImm0(Inst, offset);

	// You shouldn't load to the same register twice in an instruction...
	if (IsLoad && Rt == Rt2)
		return SoftFail;

	// ... or do any operation that writes-back to a transfer register. But note
	// that "stp xzr, xzr, [sp], #4" is fine because xzr and sp are different.
	if (NeedsDisjointWritebackTransfer && Rn != 31 && (Rt == Rn || Rt2 == Rn))
		return SoftFail;

	return Success;
}

static DecodeStatus DecodeAddSubERegInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Addr,
		const void *Decoder)
{
  unsigned Rd, Rn, Rm;
  unsigned extend = fieldFromInstruction(insn, 10, 6);
  unsigned shift = extend & 0x7;

  if (shift > 4)
    return Fail;

  Rd = fieldFromInstruction(insn, 0, 5);
  Rn = fieldFromInstruction(insn, 5, 5);
  Rm = fieldFromInstruction(insn, 16, 5);

  switch (MCInst_getOpcode(Inst)) {
  default:
    return Fail;
  case AArch64_ADDWrx:
  case AArch64_SUBWrx:
    DecodeGPR32spRegisterClass(Inst, Rd, Addr, Decoder);
    DecodeGPR32spRegisterClass(Inst, Rn, Addr, Decoder);
    DecodeGPR32RegisterClass(Inst, Rm, Addr, Decoder);
    break;
  case AArch64_ADDSWrx:
  case AArch64_SUBSWrx:
    DecodeGPR32RegisterClass(Inst, Rd, Addr, Decoder);
    DecodeGPR32spRegisterClass(Inst, Rn, Addr, Decoder);
    DecodeGPR32RegisterClass(Inst, Rm, Addr, Decoder);
    break;
  case AArch64_ADDXrx:
  case AArch64_SUBXrx:
    DecodeGPR64spRegisterClass(Inst, Rd, Addr, Decoder);
    DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder);
    DecodeGPR32RegisterClass(Inst, Rm, Addr, Decoder);
    break;
  case AArch64_ADDSXrx:
  case AArch64_SUBSXrx:
    DecodeGPR64RegisterClass(Inst, Rd, Addr, Decoder);
    DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder);
    DecodeGPR32RegisterClass(Inst, Rm, Addr, Decoder);
    break;
  case AArch64_ADDXrx64:
  case AArch64_SUBXrx64:
    DecodeGPR64spRegisterClass(Inst, Rd, Addr, Decoder);
    DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder);
    DecodeGPR64RegisterClass(Inst, Rm, Addr, Decoder);
    break;
  case AArch64_SUBSXrx64:
  case AArch64_ADDSXrx64:
    DecodeGPR64RegisterClass(Inst, Rd, Addr, Decoder);
    DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder);
    DecodeGPR64RegisterClass(Inst, Rm, Addr, Decoder);
    break;
  }

  MCOperand_CreateImm0(Inst, extend);
  return Success;
}

static DecodeStatus DecodeLogicalImmInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Addr,
		const void *Decoder)
{
	unsigned Rd = fieldFromInstruction(insn, 0, 5);
	unsigned Rn = fieldFromInstruction(insn, 5, 5);
	unsigned Datasize = fieldFromInstruction(insn, 31, 1);
	unsigned imm;

	if (Datasize) {
		if (MCInst_getOpcode(Inst) == AArch64_ANDSXri)
			DecodeGPR64RegisterClass(Inst, Rd, Addr, Decoder);
		else
			DecodeGPR64spRegisterClass(Inst, Rd, Addr, Decoder);
		DecodeGPR64RegisterClass(Inst, Rn, Addr, Decoder);
		imm = fieldFromInstruction(insn, 10, 13);
		if (!AArch64_AM_isValidDecodeLogicalImmediate(imm, 64))
			return Fail;
	} else {
		if (MCInst_getOpcode(Inst) == AArch64_ANDSWri)
			DecodeGPR32RegisterClass(Inst, Rd, Addr, Decoder);
		else
			DecodeGPR32spRegisterClass(Inst, Rd, Addr, Decoder);
		DecodeGPR32RegisterClass(Inst, Rn, Addr, Decoder);
		imm = fieldFromInstruction(insn, 10, 12);
		if (!AArch64_AM_isValidDecodeLogicalImmediate(imm, 32))
			return Fail;
	}

	MCOperand_CreateImm0(Inst, imm);
	return Success;
}

static DecodeStatus DecodeModImmInstruction(MCInst *Inst, uint32_t insn,
		uint64_t Addr,
		const void *Decoder)
{
	unsigned Rd = fieldFromInstruction(insn, 0, 5);
	unsigned cmode = fieldFromInstruction(insn, 12, 4);
	unsigned imm = fieldFromInstruction(insn, 16, 3) << 5;
	imm |= fieldFromInstruction(insn, 5, 5);

	if (MCInst_getOpcode(Inst) == AArch64_MOVID)
		DecodeFPR64RegisterClass(Inst, Rd, Addr, Decoder);
	else
		DecodeVectorRegisterClass(Inst, Rd, Addr, Decoder);

	MCOperand_CreateImm0(Inst, imm);

	switch (MCInst_getOpcode(Inst)) {
		default:
			break;
		case AArch64_MOVIv4i16:
		case AArch64_MOVIv8i16:
		case AArch64_MVNIv4i16:
		case AArch64_MVNIv8i16:
		case AArch64_MOVIv2i32:
		case AArch64_MOVIv4i32:
		case AArch64_MVNIv2i32:
		case AArch64_MVNIv4i32:
			MCOperand_CreateImm0(Inst, (cmode & 6) << 2);
			break;
		case AArch64_MOVIv2s_msl:
		case AArch64_MOVIv4s_msl:
		case AArch64_MVNIv2s_msl:
		case AArch64_MVNIv4s_msl:
			MCOperand_CreateImm0(Inst, cmode & 1 ? 0x110 : 0x108);
			break;
	}

	return Success;
}

static DecodeStatus DecodeModImmTiedInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Addr,
		const void *Decoder)
{
	unsigned Rd = fieldFromInstruction(insn, 0, 5);
	unsigned cmode = fieldFromInstruction(insn, 12, 4);
	unsigned imm = fieldFromInstruction(insn, 16, 3) << 5;
	imm |= fieldFromInstruction(insn, 5, 5);

	// Tied operands added twice.
	DecodeVectorRegisterClass(Inst, Rd, Addr, Decoder);
	DecodeVectorRegisterClass(Inst, Rd, Addr, Decoder);

	MCOperand_CreateImm0(Inst, imm);
	MCOperand_CreateImm0(Inst, (cmode & 6) << 2);

	return Success;
}

static DecodeStatus DecodeAdrInstruction(MCInst *Inst, uint32_t insn,
		uint64_t Addr, const void *Decoder)
{
	unsigned Rd = fieldFromInstruction(insn, 0, 5);
	int64_t imm = fieldFromInstruction(insn, 5, 19) << 2;
	imm |= fieldFromInstruction(insn, 29, 2);

	// Sign-extend the 21-bit immediate.
	if (imm & (1 << (21 - 1)))
		imm |= ~((1LL << 21) - 1);

	DecodeGPR64RegisterClass(Inst, Rd, Addr, Decoder);
	//if (!Dis->tryAddingSymbolicOperand(Inst, imm, Addr, Fail, 0, 4))
	MCOperand_CreateImm0(Inst, imm);

	return Success;
}

static DecodeStatus DecodeBaseAddSubImm(MCInst *Inst, uint32_t insn,
		uint64_t Addr, const void *Decoder)
{
	unsigned Rd = fieldFromInstruction(insn, 0, 5);
	unsigned Rn = fieldFromInstruction(insn, 5, 5);
	unsigned Imm = fieldFromInstruction(insn, 10, 14);
	unsigned S = fieldFromInstruction(insn, 29, 1);
	unsigned Datasize = fieldFromInstruction(insn, 31, 1);

	unsigned ShifterVal = (Imm >> 12) & 3;
	unsigned ImmVal = Imm & 0xFFF;

	if (ShifterVal != 0 && ShifterVal != 1)
		return Fail;

	if (Datasize) {
		if (Rd == 31 && !S)
			DecodeGPR64spRegisterClass(Inst, Rd, Addr, Decoder);
		else
			DecodeGPR64RegisterClass(Inst, Rd, Addr, Decoder);
		DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder);
	} else {
		if (Rd == 31 && !S)
			DecodeGPR32spRegisterClass(Inst, Rd, Addr, Decoder);
		else
			DecodeGPR32RegisterClass(Inst, Rd, Addr, Decoder);
		DecodeGPR32spRegisterClass(Inst, Rn, Addr, Decoder);
	}

	//if (!Dis->tryAddingSymbolicOperand(Inst, Imm, Addr, Fail, 0, 4))
	MCOperand_CreateImm0(Inst, ImmVal);
	MCOperand_CreateImm0(Inst, 12 * ShifterVal);
	return Success;
}

static DecodeStatus DecodeUnconditionalBranch(MCInst *Inst, uint32_t insn,
		uint64_t Addr,
		const void *Decoder)
{
	int64_t imm = fieldFromInstruction(insn, 0, 26);

	// Sign-extend the 26-bit immediate.
	if (imm & (1 << (26 - 1)))
		imm |= ~((1LL << 26) - 1);

	// if (!Dis->tryAddingSymbolicOperand(Inst, imm << 2, Addr, true, 0, 4))
	MCOperand_CreateImm0(Inst, imm);

	return Success;
}

static DecodeStatus DecodeSystemPStateInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Addr,
		const void *Decoder)
{
	uint32_t op1 = fieldFromInstruction(insn, 16, 3);
	uint32_t op2 = fieldFromInstruction(insn, 5, 3);
	uint32_t crm = fieldFromInstruction(insn, 8, 4);
	bool ValidNamed;
	uint32_t pstate_field = (op1 << 3) | op2;

	MCOperand_CreateImm0(Inst, pstate_field);
	MCOperand_CreateImm0(Inst, crm);

	A64NamedImmMapper_toString(&A64PState_PStateMapper, pstate_field, &ValidNamed);

	return ValidNamed ? Success : Fail;
}

static DecodeStatus DecodeTestAndBranch(MCInst *Inst, uint32_t insn,
		uint64_t Addr, const void *Decoder)
{
	uint32_t Rt = fieldFromInstruction(insn, 0, 5);
	uint32_t bit = fieldFromInstruction(insn, 31, 1) << 5;
	uint32_t dst = fieldFromInstruction(insn, 5, 14);

	bit |= fieldFromInstruction(insn, 19, 5);

	// Sign-extend 14-bit immediate.
	if (dst & (1 << (14 - 1)))
		dst |= ~((1LL << 14) - 1);

	if (fieldFromInstruction(insn, 31, 1) == 0)
		DecodeGPR32RegisterClass(Inst, Rt, Addr, Decoder);
	else
		DecodeGPR64RegisterClass(Inst, Rt, Addr, Decoder);

	MCOperand_CreateImm0(Inst, bit);
	//if (!Dis->tryAddingSymbolicOperand(Inst, dst << 2, Addr, true, 0, 4))
	MCOperand_CreateImm0(Inst, dst);

	return Success;
}

void AArch64_init(MCRegisterInfo *MRI)
{
	/*
		InitMCRegisterInfo(AArch64RegDesc, 420,
			RA, PC,
			AArch64MCRegisterClasses, 43,
			AArch64RegUnitRoots, 66, AArch64RegDiffLists,
			AArch64RegStrings,
			AArch64SubRegIdxLists, 53,
			AArch64SubRegIdxRanges,
			AArch64RegEncodingTable);
	*/

	MCRegisterInfo_InitMCRegisterInfo(MRI, AArch64RegDesc, 420,
			0, 0, 
			AArch64MCRegisterClasses, 43,
			0, 0, AArch64RegDiffLists,
			0, 
			AArch64SubRegIdxLists, 53,
			0);
}

#endif
