//===- 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-2019 */

#ifdef CAPSTONE_HAS_ARM64

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

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

#include "AArch64Disassembler.h"

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

#include "AArch64AddressingModes.h"
#include "AArch64BaseInfo.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 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 DecodeGPR64x8ClassRegisterClass(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 DecodeMatrixIndexGPR32_12_15RegisterClass(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 DecodeZPRRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeZPR_4bRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeZPR_3bRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeZPR2RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeZPR3RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeZPR4RegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeMatrixTile(MCInst *Inst, unsigned RegNo, 
		uint64_t Address, const void *Decoder, unsigned NumBitsForTile);
static DecodeStatus DecodeMatrixTileListRegisterClass(MCInst *Inst,
        unsigned RegMask, uint64_t Address, const void *Decoder);
static DecodeStatus DecodePPRRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodePPR_3bRegisterClass(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 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 DecodeAuthLoadInstruction(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 DecodeAddSubImmShift(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 DecodeStatus DecodeWSeqPairsClassRegisterClass(MCInst *Inst,
		unsigned RegNo, uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeXSeqPairsClassRegisterClass(MCInst *Inst,
		unsigned RegNo, uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeSVELogicalImmInstruction(MCInst *Inst, uint32_t insn,
		uint64_t Address, const void *Decoder);
static DecodeStatus DecodeSImm(MCInst *Inst, uint64_t Imm, uint64_t Address,
		const void *Decoder, int Bits);
static DecodeStatus DecodeImm8OptLsl(MCInst *Inst, unsigned Imm, uint64_t Addr,
		const void *Decoder, int ElementWidth);
static DecodeStatus DecodeSVEIncDecImm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeThreeAddrSRegInstruction(MCInst *Inst,
		uint32_t insn, uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeGPR64commonRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeFPR128_loRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeSVCROp(MCInst *Inst, unsigned Imm, uint64_t Address, 
		const void *Decoder);
static DecodeStatus DecodeCPYMemOpInstruction(MCInst *Inst, uint32_t insn,
        uint64_t Addr, const void *Decoder);
static DecodeStatus DecodeSETMemOpInstruction(MCInst *Inst, uint32_t insn,
        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
uint64_t AArch64_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, offsetof(cs_detail, arm64)+sizeof(cs_arm64));
		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_4(DecoderTable32, MI, insn, Address);
	// If Decoding fails initially, try Fallback table.
	if(result == MCDisassembler_Fail){
		result = decodeInstruction_4(DecoderTableFallback32, MI, insn, Address);	
	}

	// Init new MCOperand to be used in switch below.
	// Kind RegVal set inside a case when needed.
	MCOperand op_storage;
	MCOperand *Op = &op_storage;
	switch (MCInst_getOpcode(MI)) {
    	default:
    	  break;
    	// For Scalable Matrix Extension (SME) instructions that have an implicit
    	// operand for the accumulator (ZA) which isn't encoded, manually insert
    	// operand.
    	case AArch64_LDR_ZA:
    	case AArch64_STR_ZA: {
		  Op->Kind = kRegister;
		  Op->RegVal = AArch64_ZA;
		  MCInst_insert0(MI, 0, Op);
    	  // Spill and fill instructions have a single immediate used for both the
    	  // vector select offset and optional memory offset. Replicate the decoded
    	  // immediate.
    	  MCOperand *Imm4Op = MCInst_getOperand(MI, 2);
    	//   assert(MCOperand_isImm(Imm4Op) && "Unexpected operand type!");
    	  MCInst_addOperand2(MI, Imm4Op);
    	  break;
    	}
    	case AArch64_LD1_MXIPXX_H_B:
    	case AArch64_LD1_MXIPXX_V_B:
    	case AArch64_ST1_MXIPXX_H_B:
    	case AArch64_ST1_MXIPXX_V_B:
    	case AArch64_INSERT_MXIPZ_H_B:
    	case AArch64_INSERT_MXIPZ_V_B:
    	  // e.g.
    	  // MOVA ZA0<HV>.B[<Ws>, <imm>], <Pg>/M, <Zn>.B
    	  //      ^ insert implicit 8-bit element tile
		  Op->Kind = kRegister;
		  Op->RegVal = AArch64_ZAB0;
		  MCInst_insert0(MI, 0, Op);
    	  break;
    	case AArch64_EXTRACT_ZPMXI_H_B:
    	case AArch64_EXTRACT_ZPMXI_V_B:
    	  // MOVA <Zd>.B, <Pg>/M, ZA0<HV>.B[<Ws>, <imm>]
    	  //                      ^ insert implicit 8-bit element tile
		  Op->Kind = kRegister;
		  Op->RegVal = AArch64_ZAB0;
		  MCInst_insert0(MI, 2, Op);
    	  break;
    	case AArch64_LD1_MXIPXX_H_Q:
    	case AArch64_LD1_MXIPXX_V_Q:
    	case AArch64_ST1_MXIPXX_H_Q:
    	case AArch64_ST1_MXIPXX_V_Q:
    	  // 128-bit load/store have implicit zero vector index.
		  Op->Kind = kImmediate;
		  Op->ImmVal = 0;
		  MCInst_insert0(MI, 2, Op);
    	  break;
    	// 128-bit mova have implicit zero vector index.
    	case AArch64_INSERT_MXIPZ_H_Q:
    	case AArch64_INSERT_MXIPZ_V_Q:
		  Op->Kind = kImmediate;
		  Op->ImmVal = 0;
		  MCInst_insert0(MI, 2, Op);
    	  break;
    	case AArch64_EXTRACT_ZPMXI_H_Q:
    	case AArch64_EXTRACT_ZPMXI_V_Q:
		  Op->Kind = kImmediate;
		  Op->ImmVal = 0;
		  MCInst_addOperand2(MI, Op);
    	  break;
    	case AArch64_SMOVvi8to32_idx0:
    	case AArch64_SMOVvi8to64_idx0:
    	case AArch64_SMOVvi16to32_idx0:
    	case AArch64_SMOVvi16to64_idx0:
    	case AArch64_SMOVvi32to64_idx0:
    	case AArch64_UMOVvi8_idx0:
    	case AArch64_UMOVvi16_idx0:
    	case AArch64_UMOVvi32_idx0:
    	case AArch64_UMOVvi64_idx0:
		  Op->Kind = kImmediate;
		  Op->ImmVal = 0;
		  MCInst_addOperand2(MI, Op);
    	  break;
    }

	if (result != MCDisassembler_Fail) {
		*Size = 4;

		return result;
	}

	// invalid code
	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 DecodeGPR64commonRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Addr, const void *Decoder)
{
	unsigned Register;

	if (RegNo > 30)
		return Fail;

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

	return Success;
}

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 const unsigned GPR64x8DecoderTable[] = {
	AArch64_X0_X1_X2_X3_X4_X5_X6_X7, AArch64_X2_X3_X4_X5_X6_X7_X8_X9, 
	AArch64_X4_X5_X6_X7_X8_X9_X10_X11, AArch64_X6_X7_X8_X9_X10_X11_X12_X13, 
	AArch64_X8_X9_X10_X11_X12_X13_X14_X15, AArch64_X10_X11_X12_X13_X14_X15_X16_X17, 
	AArch64_X12_X13_X14_X15_X16_X17_X18_X19, AArch64_X14_X15_X16_X17_X18_X19_X20_X21, 
	AArch64_X16_X17_X18_X19_X20_X21_X22_X23, AArch64_X18_X19_X20_X21_X22_X23_X24_X25, 
	AArch64_X20_X21_X22_X23_X24_X25_X26_X27, AArch64_X22_X23_X24_X25_X26_X27_X28_FP
};

static DecodeStatus DecodeGPR64x8ClassRegisterClass(MCInst *Inst, unsigned RegNo, 
		uint64_t Address, const void *Decoder) 
{	
	if (RegNo > 22)
		return Fail;
	if (RegNo & 1)
		return Fail;
	
	unsigned Register = GPR64x8DecoderTable[RegNo >> 1];
	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 MatrixIndexGPR32_12_15DecoderTable[] = {
	AArch64_W12, AArch64_W13, AArch64_W14, AArch64_W15
};

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

	if (RegNo > 3)
    	return Fail;
	
	Register = MatrixIndexGPR32_12_15DecoderTable[RegNo];
	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 ZPRDecoderTable[] = {
    AArch64_Z0,  AArch64_Z1,  AArch64_Z2,  AArch64_Z3,
    AArch64_Z4,  AArch64_Z5,  AArch64_Z6,  AArch64_Z7,
    AArch64_Z8,  AArch64_Z9,  AArch64_Z10, AArch64_Z11,
    AArch64_Z12, AArch64_Z13, AArch64_Z14, AArch64_Z15,
    AArch64_Z16, AArch64_Z17, AArch64_Z18, AArch64_Z19,
    AArch64_Z20, AArch64_Z21, AArch64_Z22, AArch64_Z23,
    AArch64_Z24, AArch64_Z25, AArch64_Z26, AArch64_Z27,
    AArch64_Z28, AArch64_Z29, AArch64_Z30, AArch64_Z31
};

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

	if (RegNo > 31)
		return Fail;

	Register = ZPRDecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);

	return Success;
}

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

	return DecodeZPRRegisterClass(Inst, RegNo, Address, Decoder);
}

static DecodeStatus DecodeZPR_3bRegisterClass(MCInst *Inst, unsigned RegNo,
		uint64_t Address, const void *Decoder)
{
	if (RegNo > 7)
		return Fail;

	return DecodeZPRRegisterClass(Inst, RegNo, Address, Decoder);
}

static const unsigned ZZDecoderTable[] = {
	AArch64_Z0_Z1,   AArch64_Z1_Z2,   AArch64_Z2_Z3,   AArch64_Z3_Z4,
	AArch64_Z4_Z5,   AArch64_Z5_Z6,   AArch64_Z6_Z7,   AArch64_Z7_Z8,
	AArch64_Z8_Z9,   AArch64_Z9_Z10,  AArch64_Z10_Z11, AArch64_Z11_Z12,
	AArch64_Z12_Z13, AArch64_Z13_Z14, AArch64_Z14_Z15, AArch64_Z15_Z16,
	AArch64_Z16_Z17, AArch64_Z17_Z18, AArch64_Z18_Z19, AArch64_Z19_Z20,
	AArch64_Z20_Z21, AArch64_Z21_Z22, AArch64_Z22_Z23, AArch64_Z23_Z24,
	AArch64_Z24_Z25, AArch64_Z25_Z26, AArch64_Z26_Z27, AArch64_Z27_Z28,
	AArch64_Z28_Z29, AArch64_Z29_Z30, AArch64_Z30_Z31, AArch64_Z31_Z0
};

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

	if (RegNo > 31)
		return Fail;

	Register = ZZDecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);

	return Success;
}

static const unsigned ZZZDecoderTable[] = {
	AArch64_Z0_Z1_Z2,    AArch64_Z1_Z2_Z3,    AArch64_Z2_Z3_Z4,
	AArch64_Z3_Z4_Z5,    AArch64_Z4_Z5_Z6,    AArch64_Z5_Z6_Z7,
	AArch64_Z6_Z7_Z8,    AArch64_Z7_Z8_Z9,    AArch64_Z8_Z9_Z10,
	AArch64_Z9_Z10_Z11,  AArch64_Z10_Z11_Z12, AArch64_Z11_Z12_Z13,
	AArch64_Z12_Z13_Z14, AArch64_Z13_Z14_Z15, AArch64_Z14_Z15_Z16,
	AArch64_Z15_Z16_Z17, AArch64_Z16_Z17_Z18, AArch64_Z17_Z18_Z19,
	AArch64_Z18_Z19_Z20, AArch64_Z19_Z20_Z21, AArch64_Z20_Z21_Z22,
	AArch64_Z21_Z22_Z23, AArch64_Z22_Z23_Z24, AArch64_Z23_Z24_Z25,
	AArch64_Z24_Z25_Z26, AArch64_Z25_Z26_Z27, AArch64_Z26_Z27_Z28,
	AArch64_Z27_Z28_Z29, AArch64_Z28_Z29_Z30, AArch64_Z29_Z30_Z31,
	AArch64_Z30_Z31_Z0,  AArch64_Z31_Z0_Z1
};

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

	if (RegNo > 31)
		return Fail;

	Register = ZZZDecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);

	return Success;
}

static const unsigned ZZZZDecoderTable[] = {
	AArch64_Z0_Z1_Z2_Z3,     AArch64_Z1_Z2_Z3_Z4,     AArch64_Z2_Z3_Z4_Z5,
	AArch64_Z3_Z4_Z5_Z6,     AArch64_Z4_Z5_Z6_Z7,     AArch64_Z5_Z6_Z7_Z8,
	AArch64_Z6_Z7_Z8_Z9,     AArch64_Z7_Z8_Z9_Z10,    AArch64_Z8_Z9_Z10_Z11,
	AArch64_Z9_Z10_Z11_Z12,  AArch64_Z10_Z11_Z12_Z13, AArch64_Z11_Z12_Z13_Z14,
	AArch64_Z12_Z13_Z14_Z15, AArch64_Z13_Z14_Z15_Z16, AArch64_Z14_Z15_Z16_Z17,
	AArch64_Z15_Z16_Z17_Z18, AArch64_Z16_Z17_Z18_Z19, AArch64_Z17_Z18_Z19_Z20,
	AArch64_Z18_Z19_Z20_Z21, AArch64_Z19_Z20_Z21_Z22, AArch64_Z20_Z21_Z22_Z23,
	AArch64_Z21_Z22_Z23_Z24, AArch64_Z22_Z23_Z24_Z25, AArch64_Z23_Z24_Z25_Z26,
	AArch64_Z24_Z25_Z26_Z27, AArch64_Z25_Z26_Z27_Z28, AArch64_Z26_Z27_Z28_Z29,
	AArch64_Z27_Z28_Z29_Z30, AArch64_Z28_Z29_Z30_Z31, AArch64_Z29_Z30_Z31_Z0,
	AArch64_Z30_Z31_Z0_Z1,   AArch64_Z31_Z0_Z1_Z2
};

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

	if (RegNo > 31)
		return Fail;

	Register = ZZZZDecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);

	return Success;
}

static DecodeStatus DecodeMatrixTileListRegisterClass(MCInst *Inst,
		unsigned RegMask, uint64_t Address, const void *Decoder) {
	if (RegMask > 0xFF)
    	return Fail;
	
	MCOperand_CreateImm0(Inst, RegMask);
	return Success;
}

static const unsigned MatrixZATileDecoderTable[] = {
	AArch64_ZAB0,
    AArch64_ZAH0, AArch64_ZAH1,
    AArch64_ZAS0, AArch64_ZAS1, AArch64_ZAS2, AArch64_ZAS3,
    AArch64_ZAD0, AArch64_ZAD1, AArch64_ZAD2, AArch64_ZAD3,
    AArch64_ZAD4, AArch64_ZAD5, AArch64_ZAD6, AArch64_ZAD7,
    AArch64_ZAQ0, AArch64_ZAQ1, AArch64_ZAQ2, AArch64_ZAQ3,
    AArch64_ZAQ4, AArch64_ZAQ5, AArch64_ZAQ6, AArch64_ZAQ7,
    AArch64_ZAQ8, AArch64_ZAQ9, AArch64_ZAQ10, AArch64_ZAQ11,
    AArch64_ZAQ12, AArch64_ZAQ13, AArch64_ZAQ14, AArch64_ZAQ15
};

static DecodeStatus DecodeMatrixTile(MCInst *Inst, unsigned RegNo,
		uint64_t Address, const void *Decoder, unsigned NumBitsForTile) {
	unsigned LastReg = (1 << NumBitsForTile) - 1;
	if (RegNo > LastReg)
    	return Fail;

	// Convert original 2D indexes into 1D table index
	unsigned index = 0;
	switch (NumBitsForTile)
	{
	case 0:
		// Only a single Byte tile at beginning of list so index = 0
		break;
	case 1:
		index = 1 + RegNo;
		break;
	case 2:
		index = 3 + RegNo;
		break;
	case 3:
		index = 7 + RegNo;
		break;
	case 4:
		index = 15 + RegNo;
		break;
	default:
		break;
	}

	MCOperand_CreateReg0(Inst, MatrixZATileDecoderTable[index]);
	return Success;
}


static const unsigned PPRDecoderTable[] = {
	AArch64_P0,  AArch64_P1,  AArch64_P2,  AArch64_P3,
	AArch64_P4,  AArch64_P5,  AArch64_P6,  AArch64_P7,
	AArch64_P8,  AArch64_P9,  AArch64_P10, AArch64_P11,
	AArch64_P12, AArch64_P13, AArch64_P14, AArch64_P15
};

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

	if (RegNo > 15)
		return Fail;

	Register = PPRDecoderTable[RegNo];
	MCOperand_CreateReg0(Inst, Register);

	return Success;
}

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

	// Just reuse the PPR decode table
	return DecodePPRRegisterClass(Inst, RegNo, Addr, Decoder);
}

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)
{
	MCOperand_CreateImm0(Inst, Imm);

	// Every system register in the encoding space is valid with the syntax
	// S<op0>_<op1>_<Cn>_<Cm>_<op2>, so decoding system registers always succeeds.
	return Success;
}

static DecodeStatus DecodeMSRSystemRegister(MCInst *Inst, unsigned Imm,
		uint64_t Address, const void *Decoder)
{
	MCOperand_CreateImm0(Inst, Imm);

	return Success;
}

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_4(Insn, 0, 5);
	unsigned Rn = fieldFromInstruction_4(Insn, 5, 5);
	unsigned IsToVec = fieldFromInstruction_4(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_4(insn, 0, 5);
	unsigned Rn = fieldFromInstruction_4(insn, 5, 5);
	unsigned Rm = fieldFromInstruction_4(insn, 16, 5);
	unsigned shiftHi = fieldFromInstruction_4(insn, 22, 2);
	unsigned shiftLo = fieldFromInstruction_4(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_4(insn, 0, 5);
	unsigned imm = fieldFromInstruction_4(insn, 5, 16);
	unsigned shift = fieldFromInstruction_4(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_4(insn, 0, 5);
	unsigned Rn = fieldFromInstruction_4(insn, 5, 5);
	unsigned offset = fieldFromInstruction_4(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, IsIndexed, IsFP;
	unsigned Rt = fieldFromInstruction_4(insn, 0, 5);
	unsigned Rn = fieldFromInstruction_4(insn, 5, 5);
	int64_t offset = fieldFromInstruction_4(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:
		case AArch64_STLURBi:
		case AArch64_STLURHi:
		case AArch64_STLURWi:
		case AArch64_LDAPURBi:
		case AArch64_LDAPURSBWi:
		case AArch64_LDAPURHi:
		case AArch64_LDAPURSHWi:
		case AArch64_LDAPURi:
			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:
		case AArch64_LDAPURSWi:
		case AArch64_LDAPURSHXi:
		case AArch64_LDAPURSBXi:
		case AArch64_STLURXi:
		case AArch64_LDAPURXi:
			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_4(insn, 22, 1) != 0;
	IsIndexed = fieldFromInstruction_4(insn, 10, 2) != 0;
	IsFP = fieldFromInstruction_4(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_4(insn, 0, 5);
	unsigned Rn = fieldFromInstruction_4(insn, 5, 5);
	unsigned Rt2 = fieldFromInstruction_4(insn, 10, 5);
	unsigned Rs = fieldFromInstruction_4(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:
		case AArch64_STLLRW:
		case AArch64_STLLRB:
		case AArch64_STLLRH:
		case AArch64_LDLARW:
		case AArch64_LDLARB:
		case AArch64_LDLARH:
			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:
		case AArch64_LDLARX:
		case AArch64_STLLRX:
			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_4(insn, 0, 5);
	unsigned Rn = fieldFromInstruction_4(insn, 5, 5);
	unsigned Rt2 = fieldFromInstruction_4(insn, 10, 5);
	int32_t offset = fieldFromInstruction_4(insn, 15, 7);
	bool IsLoad = fieldFromInstruction_4(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 DecodeAuthLoadInstruction(MCInst *Inst, uint32_t insn, 
		uint64_t Addr, const void *Decoder) 
{
	unsigned Rt = fieldFromInstruction_4(insn, 0, 5);
	unsigned Rn = fieldFromInstruction_4(insn, 5, 5);
	uint64_t offset = fieldFromInstruction_4(insn, 22, 1) << 9 |
						fieldFromInstruction_4(insn, 12, 9);
	unsigned writeback = fieldFromInstruction_4(insn, 11, 1);

	switch (MCInst_getOpcode(Inst)) {
	default:
		return Fail;
	case AArch64_LDRAAwriteback:
	case AArch64_LDRABwriteback:
		DecodeGPR64spRegisterClass(Inst, Rn /* writeback register */, Addr,
								Decoder);
		break;
	case AArch64_LDRAAindexed:
	case AArch64_LDRABindexed:
		break;
	}

	DecodeGPR64RegisterClass(Inst, Rt, Addr, Decoder);
	DecodeGPR64spRegisterClass(Inst, Rn, Addr, Decoder);
	DecodeSImm(Inst, offset, Addr, Decoder, 10);

	if (writeback && Rt == Rn && Rn != 31) {
		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_4(insn, 10, 6);
	unsigned shift = extend & 0x7;

	if (shift > 4)
		return Fail;

	Rd = fieldFromInstruction_4(insn, 0, 5);
	Rn = fieldFromInstruction_4(insn, 5, 5);
	Rm = fieldFromInstruction_4(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_4(insn, 0, 5);
	unsigned Rn = fieldFromInstruction_4(insn, 5, 5);
	unsigned Datasize = fieldFromInstruction_4(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_4(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_4(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_4(insn, 0, 5);
	unsigned cmode = fieldFromInstruction_4(insn, 12, 4);
	unsigned imm = fieldFromInstruction_4(insn, 16, 3) << 5;
	imm |= fieldFromInstruction_4(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_4(insn, 0, 5);
	unsigned cmode = fieldFromInstruction_4(insn, 12, 4);
	unsigned imm = fieldFromInstruction_4(insn, 16, 3) << 5;
	imm |= fieldFromInstruction_4(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_4(insn, 0, 5);
	int64_t imm = fieldFromInstruction_4(insn, 5, 19) << 2;
	imm |= fieldFromInstruction_4(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 DecodeAddSubImmShift(MCInst *Inst, uint32_t insn,
		uint64_t Addr, const void *Decoder) 
{
	unsigned Rd = fieldFromInstruction_4(insn, 0, 5);
	unsigned Rn = fieldFromInstruction_4(insn, 5, 5);
	unsigned Imm = fieldFromInstruction_4(insn, 10, 14);
	unsigned S = fieldFromInstruction_4(insn, 29, 1);
	unsigned Datasize = fieldFromInstruction_4(insn, 31, 1);

	unsigned ShifterVal = (Imm >> 12) & 3;
	unsigned ImmVal = Imm & 0xFFF;
	//   const AArch64Disassembler *Dis =
	//       static_cast<const AArch64Disassembler *>(Decoder);

	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_4(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_4(insn, 16, 3);
	uint32_t op2 = fieldFromInstruction_4(insn, 5, 3);
	uint32_t crm = fieldFromInstruction_4(insn, 8, 4);
	uint32_t pstate_field = (op1 << 3) | op2;

	if ((pstate_field == AArch64PState_PAN  ||
		pstate_field == AArch64PState_UAO) && crm > 1)
		return Fail;

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

	if (lookupPStateByEncoding(pstate_field))
		return Success;

	return Fail;
}

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

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

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

	if (fieldFromInstruction_4(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;
}

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

	// Register number must be even (see CASP instruction)
	if (RegNo & 0x1)
		return Fail;

	Register = AArch64MCRegisterClasses[RegClassID].RegsBegin[RegNo / 2];
	MCOperand_CreateReg0(Inst, Register);

	return Success;
}

static DecodeStatus DecodeWSeqPairsClassRegisterClass(MCInst *Inst,
		unsigned RegNo, uint64_t Addr, const void *Decoder)
{
	return DecodeGPRSeqPairsClassRegisterClass(Inst,
			AArch64_WSeqPairsClassRegClassID, RegNo, Addr, Decoder);
}

static DecodeStatus DecodeXSeqPairsClassRegisterClass(MCInst *Inst,
		unsigned RegNo, uint64_t Addr, const void *Decoder)
{
	return DecodeGPRSeqPairsClassRegisterClass(Inst,
			AArch64_XSeqPairsClassRegClassID, RegNo, Addr, Decoder);
}

static DecodeStatus DecodeSVELogicalImmInstruction(MCInst *Inst, uint32_t insn,
		uint64_t Addr, const void *Decoder)
{
	unsigned Zdn = fieldFromInstruction_4(insn, 0, 5);
	unsigned imm = fieldFromInstruction_4(insn, 5, 13);

	if (!AArch64_AM_isValidDecodeLogicalImmediate(imm, 64))
		return Fail;

	// The same (tied) operand is added twice to the instruction.
	DecodeZPRRegisterClass(Inst, Zdn, Addr, Decoder);
	if (MCInst_getOpcode(Inst) != AArch64_DUPM_ZI)
		DecodeZPRRegisterClass(Inst, Zdn, Addr, Decoder);

	MCOperand_CreateImm0(Inst, imm);

	return Success;
}

static DecodeStatus DecodeSImm(MCInst *Inst, uint64_t Imm, uint64_t Address,
		const void *Decoder, int Bits)
{
	if (Imm & ~((1LL << Bits) - 1))
		return Fail;

	// Imm is a signed immediate, so sign extend it.
	if (Imm & (1 << (Bits - 1)))
		Imm |= ~((1LL << Bits) - 1);

	MCOperand_CreateImm0(Inst, Imm);

	return Success;
}

// Decode 8-bit signed/unsigned immediate for a given element width.
static DecodeStatus DecodeImm8OptLsl(MCInst *Inst, unsigned Imm, uint64_t Addr,
		const void *Decoder, int ElementWidth)
{
	unsigned Val = (uint8_t)Imm;
	unsigned Shift = (Imm & 0x100) ? 8 : 0;

	if (ElementWidth == 8 && Shift)
		return Fail;

	MCOperand_CreateImm0(Inst, Val);
	MCOperand_CreateImm0(Inst, Shift);

	return Success;
}

// Decode uimm4 ranged from 1-16.
static DecodeStatus DecodeSVEIncDecImm(MCInst *Inst, unsigned Imm,
		uint64_t Addr, const void *Decoder)
{
	MCOperand_CreateImm0(Inst, Imm + 1);

	return Success;
}

static DecodeStatus DecodeSVCROp(MCInst *Inst, unsigned Imm, uint64_t Address,
        const void *Decoder) {
	if (lookupSVCRByEncoding(Imm)) {
		MCOperand_CreateImm0(Inst, Imm);
    	return Success;
  	}
  	return Fail;
}

static DecodeStatus DecodeCPYMemOpInstruction(MCInst *Inst, uint32_t insn,
        uint64_t Addr, const void *Decoder) {
  	unsigned Rd = fieldFromInstruction_4(insn, 0, 5);
  	unsigned Rs = fieldFromInstruction_4(insn, 16, 5);
  	unsigned Rn = fieldFromInstruction_4(insn, 5, 5);

  	// None of the registers may alias: if they do, then the instruction is not
  	// merely unpredictable but actually entirely unallocated.
  	if (Rd == Rs || Rs == Rn || Rd == Rn)
    	return Fail;

  	// All three register operands are written back, so they all appear
  	// twice in the operand list, once as outputs and once as inputs.
  	if (!DecodeGPR64commonRegisterClass(Inst, Rd, Addr, Decoder) ||
    	!DecodeGPR64commonRegisterClass(Inst, Rs, Addr, Decoder) ||
      	!DecodeGPR64RegisterClass(Inst, Rn, Addr, Decoder) ||
      	!DecodeGPR64commonRegisterClass(Inst, Rd, Addr, Decoder) ||
      	!DecodeGPR64commonRegisterClass(Inst, Rs, Addr, Decoder) ||
      	!DecodeGPR64RegisterClass(Inst, Rn, Addr, Decoder))
    	return Fail;

  return Success;
}

static DecodeStatus DecodeSETMemOpInstruction(MCInst *Inst, uint32_t insn,
        uint64_t Addr, const void *Decoder) {
  	unsigned Rd = fieldFromInstruction_4(insn, 0, 5);
  	unsigned Rm = fieldFromInstruction_4(insn, 16, 5);
  	unsigned Rn = fieldFromInstruction_4(insn, 5, 5);

  	// None of the registers may alias: if they do, then the instruction is not
  	// merely unpredictable but actually entirely unallocated.
  	if (Rd == Rm || Rm == Rn || Rd == Rn)
    	return Fail;

  	// Rd and Rn (not Rm) register operands are written back, so they appear
  	// twice in the operand list, once as outputs and once as inputs.
  	if (!DecodeGPR64commonRegisterClass(Inst, Rd, Addr, Decoder) ||
      	!DecodeGPR64RegisterClass(Inst, Rn, Addr, Decoder) ||
      	!DecodeGPR64commonRegisterClass(Inst, Rd, Addr, Decoder) ||
      	!DecodeGPR64RegisterClass(Inst, Rn, Addr, Decoder) ||
      	!DecodeGPR64RegisterClass(Inst, Rm, Addr, Decoder))
    	return Fail;

  	return Success;
}

void AArch64_init(MCRegisterInfo *MRI)
{
	/*
	   InitMCRegisterInfo(AArch64RegDesc, 661,
	   		RA, PC,
			AArch64MCRegisterClasses, 100,
			AArch64RegUnitRoots, 115, AArch64RegDiffLists,
			AArch64LaneMaskLists, AArch64RegStrings, AArch64RegClassStrings,
			AArch64SubRegIdxLists, 100,
			AArch64SubRegIdxRanges, AArch64RegEncodingTable);
	*/

	MCRegisterInfo_InitMCRegisterInfo(MRI, AArch64RegDesc, 674,
			0, 0,
			AArch64MCRegisterClasses, 202,
			0, 0, AArch64RegDiffLists,
			0,
			AArch64SubRegIdxLists, 100,
			0);
}

#endif
