/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2022, */
/*    Rot127 <unisono@quyllur.org> 2022-2023 */
/* Automatically translated source file from LLVM. */

/* LLVM-commit: <commit> */
/* LLVM-tag: <tag> */

/* Only small edits allowed. */
/* For multiple similar edits, please create a Patch for the translator. */

/* Capstone's C++ file translator: */
/* https://github.com/capstone-engine/capstone/tree/next/suite/auto-sync */

//===- AArch64Disassembler.cpp - Disassembler for AArch64 -----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//
//===----------------------------------------------------------------------===//

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <capstone/platform.h>

#include "../../MCFixedLenDisassembler.h"
#include "../../MCInst.h"
#include "../../MCInstrDesc.h"
#include "../../MCRegisterInfo.h"
#include "../../LEB128.h"
#include "../../MCDisassembler.h"
#include "../../cs_priv.h"
#include "../../utils.h"
#include "AArch64AddressingModes.h"
#include "AArch64BaseInfo.h"
#include "AArch64DisassemblerExtension.h"
#include "AArch64Linkage.h"
#include "AArch64Mapping.h"

#define GET_INSTRINFO_MC_DESC
#include "AArch64GenInstrInfo.inc"

#define CONCAT(a, b) CONCAT_(a, b)
#define CONCAT_(a, b) a##_##b

#define DEBUG_TYPE "aarch64-disassembler"

// Pull DecodeStatus and its enum values into the global namespace.

// 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 DecodeFPR128_0to7RegisterClass(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 DecodeGPR64commonRegisterClass(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_8_11RegisterClass(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 DecodeZPR2Mul2RegisterClass(MCInst *Inst, unsigned RegNo,
						uint64_t Address,
						const void *Decoder);
static DecodeStatus DecodeZPR4Mul4RegisterClass(MCInst *Inst, unsigned RegNo,
						uint64_t Address,
						const void *Decoder);
static DecodeStatus DecodeZPR2StridedRegisterClass(MCInst *Inst, unsigned RegNo,
						   uint64_t Address,
						   const void *Decoder);
static DecodeStatus DecodeZPR4StridedRegisterClass(MCInst *Inst, unsigned RegNo,
						   uint64_t Address,
						   const void *Decoder);
#define DECLARE_DecodeMatrixTile(NumBitsForTile) \
	static DecodeStatus CONCAT(DecodeMatrixTile, NumBitsForTile)( \
		MCInst * Inst, unsigned RegNo, uint64_t Address, \
		const void *Decoder);
DECLARE_DecodeMatrixTile(2);
DECLARE_DecodeMatrixTile(1);
DECLARE_DecodeMatrixTile(3);
DECLARE_DecodeMatrixTile(4);

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 DecodePNRRegisterClass(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 DecodePNR_p8to15RegisterClass(MCInst *Inst, unsigned RegNo,
						  uint64_t Address,
						  const void *Decoder);
static DecodeStatus DecodePPR2RegisterClass(MCInst *Inst, unsigned RegNo,
					    uint64_t Address,
					    const void *Decoder);
static DecodeStatus DecodePPR2Mul2RegisterClass(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 DecodePCRelLabel16(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 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 DecodeSystemPStateImm0_15Instruction(MCInst *Inst,
							 uint32_t insn,
							 uint64_t Address,
							 const void *Decoder);
static DecodeStatus DecodeSystemPStateImm0_1Instruction(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 DecodeSyspXzrInstruction(MCInst *Inst, uint32_t insn,
					     uint64_t Addr,
					     const void *Decoder);
static DecodeStatus DecodeSVELogicalImmInstruction(MCInst *Inst, uint32_t insn,
						   uint64_t Address,
						   const void *Decoder);
#define DECLARE_DecodeSImm(Bits) \
	static DecodeStatus CONCAT(DecodeSImm, Bits)(MCInst * Inst, \
						     uint64_t Imm, \
						     uint64_t Address, \
						     const void *Decoder);
DECLARE_DecodeSImm(4);
DECLARE_DecodeSImm(5);
DECLARE_DecodeSImm(6);
DECLARE_DecodeSImm(8);
DECLARE_DecodeSImm(9);
DECLARE_DecodeSImm(10);

#define DECLARE_DecodeImm8OptLsl(ElementWidth) \
	static DecodeStatus CONCAT(DecodeImm8OptLsl, ElementWidth)( \
		MCInst * Inst, unsigned Imm, uint64_t Addr, \
		const void *Decoder);
DECLARE_DecodeImm8OptLsl(8);
DECLARE_DecodeImm8OptLsl(16);
DECLARE_DecodeImm8OptLsl(32);
DECLARE_DecodeImm8OptLsl(64);

static DecodeStatus DecodeSVEIncDecImm(MCInst *Inst, unsigned Imm,
				       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 DecodeStatus DecodePRFMRegInstruction(MCInst *Inst, uint32_t insn,
					     uint64_t Address,
					     const void *Decoder);

#include "AArch64GenDisassemblerTables.inc"

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

static DecodeStatus getInstruction(csh handle, const uint8_t *Bytes,
				   size_t ByteLen, MCInst *MI, uint16_t *Size,
				   uint64_t Address, void *Info)
{
	*Size = 0;
	// We want to read exactly 4 bytes of data.
	if (ByteLen < 4)
		return Fail;
	*Size = 4;

	// Encoded as a small-endian 32-bit word in the stream.
	uint32_t Insn = readBytes32(MI, Bytes);

	const uint8_t *Tables[] = { DecoderTable32, DecoderTableFallback32 };

	for (int i = 0; i < (sizeof(Tables) / sizeof(Tables[0])); ++i) {
		void *Decoder = NULL;
		DecodeStatus Result = decodeInstruction_4(Tables[i], MI, Insn,
							  Address, Decoder);

		// Table is indexed backwards
		const MCInstrDesc Desc =
			AArch64Descs.Insts[ARR_SIZE(AArch64Descs.Insts) - 1 -
					   MCInst_getOpcode(MI)];

		// For Scalable Matrix Extension (SME) instructions that have an
		// implicit operand for the accumulator (ZA) or implicit immediate zero
		// which isn't encoded, manually insert operand.
		for (unsigned j = 0; j < Desc.NumOperands; j++) {
			if (Desc.OpInfo[j].OperandType ==
			    MCOI_OPERAND_REGISTER) {
				switch (Desc.OpInfo[j].RegClass) {
				default:
					break;
				case AArch64_MPRRegClassID:
					MCInst_insert0(MI, j,
						       MCOperand_CreateReg1(
							       MI, AArch64_ZA));
					break;
				case AArch64_MPR8RegClassID:
					MCInst_insert0(MI, j,
						       MCOperand_CreateReg1(
							       MI,
							       AArch64_ZAB0));
					break;
				case AArch64_ZTRRegClassID:
					MCInst_insert0(MI, j,
						       MCOperand_CreateReg1(
							       MI,
							       AArch64_ZT0));
					break;
				}
			} else if (Desc.OpInfo[j].OperandType ==
				   AARCH64_OP_IMPLICIT_IMM_0) {
				MCInst_insert0(MI, j,
					       MCOperand_CreateImm1(MI, 0));
			}
		}

		if (MCInst_getOpcode(MI) == AArch64_LDR_ZA ||
		    MCInst_getOpcode(MI) == AArch64_STR_ZA) {
			// 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));

			MCInst_addOperand2(MI, (Imm4Op));
		}

		if (Result != MCDisassembler_Fail)
			return Result;
	}

	return MCDisassembler_Fail;
}

DecodeStatus AArch64_LLVM_getInstruction(csh handle, const uint8_t *Bytes,
					 size_t ByteLen, MCInst *MI,
					 uint16_t *Size, uint64_t Address,
					 void *Info)
{
	DecodeStatus Result = MCDisassembler_Fail;
	Result =
		getInstruction(handle, Bytes, ByteLen, MI, Size, Address, Info);
	MCInst_handleWriteback(MI, AArch64Descs.Insts,
			       ARR_SIZE(AArch64Descs.Insts));
	return Result;
}

uint64_t suggestBytesToSkip(const uint8_t *Bytes, uint64_t Address)
{
	// AArch64 instructions are always 4 bytes wide, so there's no point
	// in skipping any smaller number of bytes if an instruction can't
	// be decoded.
	return 4;
}

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

	unsigned Register = AArch64MCRegisterClasses[AArch64_FPR128RegClassID]
				    .RegsBegin[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 DecodeStatus DecodeFPR128_0to7RegisterClass(MCInst *Inst, unsigned RegNo,
						   uint64_t Addr,
						   const void *Decoder)
{
	if (RegNo > 7)
		return Fail;
	return DecodeFPR128RegisterClass(Inst, RegNo, Addr, Decoder);
}

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

	unsigned Register = AArch64MCRegisterClasses[AArch64_FPR64RegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

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

	unsigned Register = AArch64MCRegisterClasses[AArch64_FPR32RegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

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

	unsigned Register = AArch64MCRegisterClasses[AArch64_FPR16RegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

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

	unsigned Register = AArch64MCRegisterClasses[AArch64_FPR8RegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

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

	unsigned Register =
		AArch64MCRegisterClasses[AArch64_GPR64commonRegClassID]
			.RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

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

	unsigned Register = AArch64MCRegisterClasses[AArch64_GPR64RegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

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 =
		AArch64MCRegisterClasses[AArch64_GPR64x8ClassRegClassID]
			.RegsBegin[RegNo >> 1];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeGPR64spRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Addr,
					       const void *Decoder)
{
	if (RegNo > 31)
		return Fail;
	unsigned Register = AArch64MCRegisterClasses[AArch64_GPR64spRegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

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

	unsigned Register =
		AArch64MCRegisterClasses[AArch64_MatrixIndexGPR32_8_11RegClassID]
			.RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

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

	unsigned Register =
		AArch64MCRegisterClasses[AArch64_MatrixIndexGPR32_12_15RegClassID]
			.RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

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

	unsigned Register = AArch64MCRegisterClasses[AArch64_GPR32RegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

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

	unsigned Register = AArch64MCRegisterClasses[AArch64_GPR32spRegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

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

	unsigned Register =
		AArch64MCRegisterClasses[AArch64_ZPRRegClassID].RegsBegin[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 DecodeStatus DecodeZPR2RegisterClass(MCInst *Inst, unsigned RegNo,
					    uint64_t Address,
					    const void *Decoder)
{
	if (RegNo > 31)
		return Fail;
	unsigned Register = AArch64MCRegisterClasses[AArch64_ZPR2RegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeZPR3RegisterClass(MCInst *Inst, unsigned RegNo,
					    uint64_t Address,
					    const void *Decoder)
{
	if (RegNo > 31)
		return Fail;
	unsigned Register = AArch64MCRegisterClasses[AArch64_ZPR3RegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeZPR4RegisterClass(MCInst *Inst, unsigned RegNo,
					    uint64_t Address,
					    const void *Decoder)
{
	if (RegNo > 31)
		return Fail;
	unsigned Register = AArch64MCRegisterClasses[AArch64_ZPR4RegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeZPR2Mul2RegisterClass(MCInst *Inst, unsigned RegNo,
						uint64_t Address,
						const void *Decoder)
{
	if (RegNo * 2 > 30)
		return Fail;
	unsigned Register = AArch64MCRegisterClasses[AArch64_ZPR2RegClassID]
				    .RegsBegin[RegNo * 2];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeZPR4Mul4RegisterClass(MCInst *Inst, unsigned RegNo,
						uint64_t Address,
						const void *Decoder)
{
	if (RegNo * 4 > 28)
		return Fail;
	unsigned Register = AArch64MCRegisterClasses[AArch64_ZPR4RegClassID]
				    .RegsBegin[RegNo * 4];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeZPR2StridedRegisterClass(MCInst *Inst, unsigned RegNo,
						   uint64_t Address,
						   const void *Decoder)
{
	if (RegNo > 15)
		return Fail;
	unsigned Register =
		AArch64MCRegisterClasses[AArch64_ZPR2StridedRegClassID]
			.RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeZPR4StridedRegisterClass(MCInst *Inst, unsigned RegNo,
						   uint64_t Address,
						   const void *Decoder)
{
	if (RegNo > 7)
		return Fail;
	unsigned Register =
		AArch64MCRegisterClasses[AArch64_ZPR4StridedRegClassID]
			.RegsBegin[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 MCPhysReg MatrixZATileDecoderTable[5][16] = {
	{ 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 }
};

#define DEFINE_DecodeMatrixTile(NumBitsForTile) \
	static DecodeStatus CONCAT(DecodeMatrixTile, NumBitsForTile)( \
		MCInst * Inst, unsigned RegNo, uint64_t Address, \
		const void *Decoder) \
	{ \
		unsigned LastReg = (1 << NumBitsForTile) - 1; \
		if (RegNo > LastReg) \
			return Fail; \
		MCOperand_CreateReg0( \
			Inst, \
			(MatrixZATileDecoderTable[NumBitsForTile][RegNo])); \
		return Success; \
	}
DEFINE_DecodeMatrixTile(2);
DEFINE_DecodeMatrixTile(1);
DEFINE_DecodeMatrixTile(3);
DEFINE_DecodeMatrixTile(4);

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

	unsigned Register =
		AArch64MCRegisterClasses[AArch64_PPRRegClassID].RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

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

	unsigned Register =
		AArch64MCRegisterClasses[AArch64_PNRRegClassID].RegsBegin[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 DecodeStatus DecodePNR_p8to15RegisterClass(MCInst *Inst, unsigned RegNo,
						  uint64_t Addr,
						  const void *Decoder)
{
	if (RegNo > 7)
		return Fail;

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

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

	unsigned Register = AArch64MCRegisterClasses[AArch64_PPR2RegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodePPR2Mul2RegisterClass(MCInst *Inst, unsigned RegNo,
						uint64_t Address,
						const void *Decoder)
{
	if ((RegNo * 2) > 14)
		return Fail;
	unsigned Register = AArch64MCRegisterClasses[AArch64_PPR2RegClassID]
				    .RegsBegin[RegNo * 2];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeQQRegisterClass(MCInst *Inst, unsigned RegNo,
					  uint64_t Addr, const void *Decoder)
{
	if (RegNo > 31)
		return Fail;
	unsigned Register =
		AArch64MCRegisterClasses[AArch64_QQRegClassID].RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeQQQRegisterClass(MCInst *Inst, unsigned RegNo,
					   uint64_t Addr, const void *Decoder)
{
	if (RegNo > 31)
		return Fail;
	unsigned Register =
		AArch64MCRegisterClasses[AArch64_QQQRegClassID].RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeQQQQRegisterClass(MCInst *Inst, unsigned RegNo,
					    uint64_t Addr, const void *Decoder)
{
	if (RegNo > 31)
		return Fail;
	unsigned Register = AArch64MCRegisterClasses[AArch64_QQQQRegClassID]
				    .RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeDDRegisterClass(MCInst *Inst, unsigned RegNo,
					  uint64_t Addr, const void *Decoder)
{
	if (RegNo > 31)
		return Fail;
	unsigned Register =
		AArch64MCRegisterClasses[AArch64_DDRegClassID].RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeDDDRegisterClass(MCInst *Inst, unsigned RegNo,
					   uint64_t Addr, const void *Decoder)
{
	if (RegNo > 31)
		return Fail;
	unsigned Register =
		AArch64MCRegisterClasses[AArch64_DDDRegClassID].RegsBegin[RegNo];
	MCOperand_CreateReg0(Inst, (Register));
	return Success;
}

static DecodeStatus DecodeDDDDRegisterClass(MCInst *Inst, unsigned RegNo,
					    uint64_t Addr, const void *Decoder)
{
	if (RegNo > 31)
		return Fail;
	unsigned Register = AArch64MCRegisterClasses[AArch64_DDDDRegClassID]
				    .RegsBegin[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 DecodePCRelLabel16(MCInst *Inst, unsigned Imm,
				       uint64_t Addr, const void *Decoder)
{
	// Immediate is encoded as the top 16-bits of an unsigned 18-bit negative
	// PC-relative offset.
	uint64_t ImmVal = Imm;
	if (ImmVal > (1 << 16))
		return Fail;
	// Symbols are not supported by Capstone
	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);

	// No symbols supported in Capstone
	// if (!Decoder->tryAddingSymbolicOperand(
	// 		Inst, ImmVal * 4, Addr, MCInst_getOpcode(Inst) != AArch64_LDRXl, 0,
	// 		0, 4))
	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;
		// fall through
	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;
		// fall through
	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);
	// No symbols supported in Capstone
	// if (!Decoder->tryAddingSymbolicOperand(Inst, offset, Addr, Fail, 0, 0, 4))
	MCOperand_CreateImm0(Inst, (offset));
	return Success;
}

static DecodeStatus DecodeSignedLdStInstruction(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);
	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));

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

	// 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);
		// fall through
	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);
		// fall through
	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);
		// fall through
	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);
		// fall through
	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);
	int64_t offset = fieldFromInstruction_4(insn, 15, 7);
	bool IsLoad = fieldFromInstruction_4(insn, 22, 1);

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

	unsigned Opcode = MCInst_getOpcode(Inst);
	bool NeedsDisjointWritebackTransfer = false;

	// 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:
	case AArch64_STGPpre:
	case AArch64_STGPpost:
		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:
	case AArch64_STGPpre:
	case AArch64_STGPpost:
		NeedsDisjointWritebackTransfer = true;
		// fall through
	case AArch64_LDNPXi:
	case AArch64_STNPXi:
	case AArch64_LDPXi:
	case AArch64_STPXi:
	case AArch64_LDPSWi:
	case AArch64_STGPi:
		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;
		// fall through
	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);
	CONCAT(DecodeSImm, 10)(Inst, offset, Addr, Decoder);

	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 = fieldFromInstruction_4(insn, 0, 5);
	unsigned Rn = fieldFromInstruction_4(insn, 5, 5);
	unsigned Rm = fieldFromInstruction_4(insn, 16, 5);
	unsigned extend = fieldFromInstruction_4(insn, 10, 6);

	unsigned shift = extend & 0x7;
	if (shift > 4)
		return Fail;

	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
		DecodeFPR128RegisterClass(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.
	DecodeFPR128RegisterClass(Inst, Rd, Addr, Decoder);
	DecodeFPR128RegisterClass(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);
	// No symbols supported in Capstone
	// if (!Decoder->tryAddingSymbolicOperand(Inst, imm, Addr, Fail, 0, 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;

	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);
	}

	// No symbols supported in Capstone
	// if (!Decoder->tryAddingSymbolicOperand(Inst, Imm, Addr, Fail, 0, 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);

	// No symbols supported in Capstone
	// if (!Decoder->tryAddingSymbolicOperand(Inst, imm * 4, Addr, true, 0, 0, 4))
	MCOperand_CreateImm0(Inst, (imm));

	return Success;
}

static bool isInvalidPState(uint64_t Op1, uint64_t Op2)
{
	return Op1 == 0 && (Op2 == 0 || // CFINV
			    Op2 == 1 || // XAFlag
			    Op2 == 2); // AXFlag
}

static DecodeStatus DecodeSystemPStateImm0_15Instruction(MCInst *Inst,
							 uint32_t insn,
							 uint64_t Addr,
							 const void *Decoder)
{
	uint64_t op1 = fieldFromInstruction_4(insn, 16, 3);
	uint64_t op2 = fieldFromInstruction_4(insn, 5, 3);
	uint64_t imm = fieldFromInstruction_4(insn, 8, 4);
	uint64_t pstate_field = (op1 << 3) | op2;

	if (isInvalidPState(op1, op2))
		return Fail;

	MCOperand_CreateImm0(Inst, (pstate_field));
	MCOperand_CreateImm0(Inst, (imm));

	const AArch64PState_PStateImm0_15 *PState =
		AArch64PState_lookupPStateImm0_15ByEncoding(pstate_field);
	if (PState &&
	    AArch64_testFeatureList(Inst->csh->mode, PState->FeaturesRequired))
		return Success;
	return Fail;
}

static DecodeStatus DecodeSystemPStateImm0_1Instruction(MCInst *Inst,
							uint32_t insn,
							uint64_t Addr,
							const void *Decoder)
{
	uint64_t op1 = fieldFromInstruction_4(insn, 16, 3);
	uint64_t op2 = fieldFromInstruction_4(insn, 5, 3);
	uint64_t crm_high = fieldFromInstruction_4(insn, 9, 3);
	uint64_t imm = fieldFromInstruction_4(insn, 8, 1);
	uint64_t pstate_field = (crm_high << 6) | (op1 << 3) | op2;

	if (isInvalidPState(op1, op2))
		return Fail;

	MCOperand_CreateImm0(Inst, (pstate_field));
	MCOperand_CreateImm0(Inst, (imm));

	const AArch64PState_PStateImm0_1 *PState =
		AArch64PState_lookupPStateImm0_1ByEncoding(pstate_field);
	if (PState &&
	    AArch64_testFeatureList(Inst->csh->mode, PState->FeaturesRequired))
		return Success;
	return Fail;
}

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

	// 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));
	// No symbols supported in Capstone
	// if (!Decoder->tryAddingSymbolicOperand(Inst, dst * 4, Addr, true, 0, 0, 4))
	MCOperand_CreateImm0(Inst, (dst));

	return Success;
}

static DecodeStatus DecodeGPRSeqPairsClassRegisterClass(MCInst *Inst,
							unsigned RegClassID,
							unsigned RegNo,
							uint64_t Addr,
							const void *Decoder)
{
	// Register number must be even (see CASP instruction)
	if (RegNo & 0x1)
		return Fail;

	unsigned Reg =
		AArch64MCRegisterClasses[RegClassID].RegsBegin[RegNo / 2];
	MCOperand_CreateReg0(Inst, (Reg));
	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 DecodeSyspXzrInstruction(MCInst *Inst, uint32_t insn,
					     uint64_t Addr, const void *Decoder)
{
	unsigned op1 = fieldFromInstruction_4(insn, 16, 3);
	unsigned CRn = fieldFromInstruction_4(insn, 12, 4);
	unsigned CRm = fieldFromInstruction_4(insn, 8, 4);
	unsigned op2 = fieldFromInstruction_4(insn, 5, 3);
	unsigned Rt = fieldFromInstruction_4(insn, 0, 5);
	if (Rt != 0x1f)
		return Fail;

	MCOperand_CreateImm0(Inst, (op1));
	MCOperand_CreateImm0(Inst, (CRn));
	MCOperand_CreateImm0(Inst, (CRm));
	MCOperand_CreateImm0(Inst, (op2));
	DecodeGPR64RegisterClass(Inst, Rt, Addr, Decoder);

	return Success;
}

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;
}

#define DEFINE_DecodeSImm(Bits) \
	static DecodeStatus CONCAT(DecodeSImm, Bits)(MCInst * Inst, \
						     uint64_t Imm, \
						     uint64_t Address, \
						     const void *Decoder) \
	{ \
		if (Imm & ~((1LL << Bits) - 1)) \
			return Fail; \
\
		if (Imm & (1 << (Bits - 1))) \
			Imm |= ~((1LL << Bits) - 1); \
\
		MCOperand_CreateImm0(Inst, (Imm)); \
		return Success; \
	}
DEFINE_DecodeSImm(4);
DEFINE_DecodeSImm(5);
DEFINE_DecodeSImm(6);
DEFINE_DecodeSImm(8);
DEFINE_DecodeSImm(9);
DEFINE_DecodeSImm(10);

// Decode 8-bit signed/unsigned immediate for a given element width.
#define DEFINE_DecodeImm8OptLsl(ElementWidth) \
	static DecodeStatus CONCAT(DecodeImm8OptLsl, ElementWidth)( \
		MCInst * Inst, unsigned Imm, uint64_t Addr, \
		const void *Decoder) \
	{ \
		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; \
	}
DEFINE_DecodeImm8OptLsl(8);
DEFINE_DecodeImm8OptLsl(16);
DEFINE_DecodeImm8OptLsl(32);
DEFINE_DecodeImm8OptLsl(64);

// 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 (AArch64SVCR_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 MCDisassembler_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 MCDisassembler_Fail;

	return MCDisassembler_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 MCDisassembler_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 MCDisassembler_Fail;

	return MCDisassembler_Success;
}

static DecodeStatus DecodePRFMRegInstruction(MCInst *Inst, uint32_t insn,
					     uint64_t Addr, const void *Decoder)
{
	// PRFM with Rt = '11xxx' should be decoded as RPRFM.
	// Fail to decode and defer to fallback decoder table to decode RPRFM.
	unsigned Mask = 0x18;
	uint64_t Rt = fieldFromInstruction_4(insn, 0, 5);
	if ((Rt & Mask) == Mask)
		return Fail;

	uint64_t Rn = fieldFromInstruction_4(insn, 5, 5);
	uint64_t Shift = fieldFromInstruction_4(insn, 12, 1);
	uint64_t Extend = fieldFromInstruction_4(insn, 15, 1);
	uint64_t Rm = fieldFromInstruction_4(insn, 16, 5);

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

	switch (MCInst_getOpcode(Inst)) {
	default:
		return Fail;
	case AArch64_PRFMroW:
		DecodeGPR32RegisterClass(Inst, Rm, Addr, Decoder);
		break;
	case AArch64_PRFMroX:
		DecodeGPR64RegisterClass(Inst, Rm, Addr, Decoder);
		break;
	}

	DecodeMemExtend(Inst, (Extend << 1) | Shift, Addr, Decoder);

	return Success;
}
