/* radare - LGPL - Copyright 2021-2023 - junchao82@qq.com;zhaojunchao@loongson.cn love lanhy*/

#include <r_arch.h>
#include "../../include/disas-asm.h"
#include "loongarch-private.h"

struct loongarch_anal_opcode {
	const ut32 match;
	const ut32 mask; /* High 1 byte is main opcode and it must be 0xf. */
	const char * const name;
	const ut32 index;
	const ut32 r_type; /*R_ANAL_OP_TYPE*/
};

#define HT_NUM 32
struct loongarch_ASE {
	const struct loongarch_anal_opcode *const opcode;
	const struct loongarch_anal_opcode *la_opcode_ht[HT_NUM];
	ut8 opc_htab_inited;
};

#define INSNLEN 4

typedef struct plugin_data_t {
	ut64 insn_offset;
	ut8 insn_bytes[INSNLEN];
	struct loongarch_ASE la_ases[8];
} PluginData;

//use bit[30:26] to cal hash index
#define LA_INSN_HASH(insn) (((insn) & 0x7c000000) >> 26)

/**
 * sign_extend32 - sign extend a 32-bit value using specified bit as sign-bit
 * @value: value to sign extend
 * @index: 0 based bit index (0<=index<32) to sign bit
 *
 * This is safe to use for 16- and 8-bit types as well.
 */
R_UNUSED static inline st32 sign_extend32(ut32 value, int index) {
	ut8 shift = 31 - index;
	return (st32)(value << shift) >> shift;
}

/**
 * sign_extend64 - sign extend a 64-bit value using specified bit as sign-bit
 * @value: value to sign extend
 * @index: 0 based bit index (0<=index<64) to sign bit
 */
static inline st64 sign_extend64(ut64 value, int index) {
	ut8 shift = 63 - index;
	return (st64)(value << shift) >> shift;
}

#define UL(x) (x##UL)
#define ULL(x) (x##ULL)
#define LA_PFM PFMT64x

#define GENMASK_ULL(h, l) \
	(((~ULL(0)) - (ULL(1) << (l)) + 1) & \
	 (~ULL(0) >> (64 - 1 - (h))))

#define GET_BIT(op, h, l) (((op)&GENMASK_ULL(h,l))>>(l))

/*Get the reg name by index*/
#define LA_RD() loongarch_r_lp64_name[GET_BIT((opcode), 4, 0)]
#define LA_RJ() loongarch_r_lp64_name[GET_BIT((opcode), 9, 5)]
#define LA_RK() loongarch_r_lp64_name[GET_BIT((opcode), 14, 10)]

/*Get the imm in different format instructions*/
#define I_I26(op) ((((op)>>10) & 0xffff) | (((op)&0x3ff)<<16))  //26bit number
#define I_I21(op) ((((op)>>10) & 0xffff)|(((op)&0x1f)<<16))
#define I_I20(op) (((op)>>5) & 0xfffff)
#define I_I16(op) (((op)>>10) & 0xffff)
#define I_I14(op) (((op)>>10) & 0x3fff)
#define I_I12(op) (((op)>>10) & 0xfff)
#define I_I6(op) (((op)>>10) & 0x3f)
#define I_I5(op) (((op)>>10) & 0x1f)
#define I_SA2(op) (((op)>>15) & 0x3)
#define I_SA3(op) (((op)>>15) & 0x7)

#define I_I26s2(op) (I_I26((op)) << 2)
#define I_I21s2(op) (I_I21((op))<<2)
#define I_I14s2(op) (I_I14((op))<<2)
#define I_I16s2(op) (I_I16((op)) <<2)

#define I12_SX(op) sign_extend64(I_I12((op)), 11)
#define I16_SX(op) sign_extend64(I_I16((op)), 15)

/*not is esil syntax*/
#define I16s2_SX(op) sign_extend64(I_I16s2((op)), 16+1)
#define I14s2_SX(op) sign_extend64(I_I14s2((op)), 16+1)
#define I21s2_SX(op) sign_extend64(I_I21s2((op)), 21+1)
#define I26s2_SX(op) sign_extend64(I_I26s2((op)), 26+1)

#define ES_SX32(x) "32,"x",~"
#define ES_B(x) "0xff,"x",&"
#define ES_H(x) "0xffff,"x",&"
#define ES_W(x) "0xffffffff,"x",&"
#define ES_WH(x) "32,0xffffffff00000000,"x",&,>>"

/*Maybe should be moved to another file*/
typedef enum la_insn {
	LA_INS_INVALID = 0,
	LA_INS_ADDI_D,
	LA_INS_ADDI_W,
	LA_INS_ADDU16I_D,
	LA_INS_ADD_D,
	LA_INS_ADD_W,
	LA_INS_ALSL_D,
	LA_INS_ALSL_W,
	LA_INS_ALSL_WU,
	LA_INS_AMADD_D,
	LA_INS_AMADD_DB_D,
	LA_INS_AMADD_DB_W,
	LA_INS_AMADD_W,
	LA_INS_AMAND_D,
	LA_INS_AMAND_DB_D,
	LA_INS_AMAND_DB_W,
	LA_INS_AMAND_W,
	LA_INS_AMMAX_D,
	LA_INS_AMMAX_DB_D,
	LA_INS_AMMAX_DB_DU,
	LA_INS_AMMAX_DB_W,
	LA_INS_AMMAX_DB_WU,
	LA_INS_AMMAX_DU,
	LA_INS_AMMAX_W,
	LA_INS_AMMAX_WU,
	LA_INS_AMMIN_D,
	LA_INS_AMMIN_DB_D,
	LA_INS_AMMIN_DB_DU,
	LA_INS_AMMIN_DB_W,
	LA_INS_AMMIN_DB_WU,
	LA_INS_AMMIN_DU,
	LA_INS_AMMIN_W,
	LA_INS_AMMIN_WU,
	LA_INS_AMOR_D,
	LA_INS_AMOR_DB_D,
	LA_INS_AMOR_DB_W,
	LA_INS_AMOR_W,
	LA_INS_AMSWAP_D,
	LA_INS_AMSWAP_DB_D,
	LA_INS_AMSWAP_DB_W,
	LA_INS_AMSWAP_W,
	LA_INS_AMXOR_D,
	LA_INS_AMXOR_DB_D,
	LA_INS_AMXOR_DB_W,
	LA_INS_AMXOR_W,
	LA_INS_AND,
	LA_INS_ANDI,
	LA_INS_ANDN,
	LA_INS_ASRTGT_D,
	LA_INS_ASRTLE_D,
	LA_INS_B,
	LA_INS_BCEQZ,
	LA_INS_BCNEZ,
	LA_INS_BEQ,
	LA_INS_BEQZ,
	LA_INS_BGE,
	LA_INS_BGEU,
	LA_INS_BGEZ,
	LA_INS_BGT,
	LA_INS_BGTU,
	LA_INS_BGTZ,
	LA_INS_BITREV_4B,
	LA_INS_BITREV_8B,
	LA_INS_BITREV_D,
	LA_INS_BITREV_W,
	LA_INS_BL,
	LA_INS_BLE,
	LA_INS_BLEU,
	LA_INS_BLEZ,
	LA_INS_BLT,
	LA_INS_BLTU,
	LA_INS_BLTZ,
	LA_INS_BNE,
	LA_INS_BNEZ,
	LA_INS_BREAK,
	LA_INS_BSTRINS_D,
	LA_INS_BSTRINS_W,
	LA_INS_BSTRPICK_D,
	LA_INS_BSTRPICK_W,
	LA_INS_BYTEPICK_D,
	LA_INS_BYTEPICK_W,
	LA_INS_CACOP,
	LA_INS_CLO_D,
	LA_INS_CLO_W,
	LA_INS_CLZ_D,
	LA_INS_CLZ_W,
	LA_INS_CPUCFG,
	LA_INS_CRCC_W_B_W,
	LA_INS_CRCC_W_D_W,
	LA_INS_CRCC_W_H_W,
	LA_INS_CRCC_W_W_W,
	LA_INS_CRC_W_B_W,
	LA_INS_CRC_W_D_W,
	LA_INS_CRC_W_H_W,
	LA_INS_CRC_W_W_W,
	LA_INS_CSRRD,
	LA_INS_CSRWR,
	LA_INS_CSRXCHG,
	LA_INS_CTO_D,
	LA_INS_CTO_W,
	LA_INS_CTZ_D,
	LA_INS_CTZ_W,
	LA_INS_DBAR,
	LA_INS_DBCL,
	LA_INS_DIV_D,
	LA_INS_DIV_DU,
	LA_INS_DIV_W,
	LA_INS_DIV_WU,
	LA_INS_ERTN,
	LA_INS_EXT_W_B,
	LA_INS_EXT_W_H,
	LA_INS_FABS_D,
	LA_INS_FABS_S,
	LA_INS_FADD_D,
	LA_INS_FADD_S,
	LA_INS_FCLASS_D,
	LA_INS_FCLASS_S,
	LA_INS_FCMP_CAF_D,
	LA_INS_FCMP_CAF_S,
	LA_INS_FCMP_CEQ_D,
	LA_INS_FCMP_CEQ_S,
	LA_INS_FCMP_CLE_D,
	LA_INS_FCMP_CLE_S,
	LA_INS_FCMP_CLT_D,
	LA_INS_FCMP_CLT_S,
	LA_INS_FCMP_CNE_D,
	LA_INS_FCMP_CNE_S,
	LA_INS_FCMP_COR_D,
	LA_INS_FCMP_COR_S,
	LA_INS_FCMP_CUEQ_D,
	LA_INS_FCMP_CUEQ_S,
	LA_INS_FCMP_CUGE_D,
	LA_INS_FCMP_CUGE_S,
	LA_INS_FCMP_CUGT_D,
	LA_INS_FCMP_CUGT_S,
	LA_INS_FCMP_CULE_D,
	LA_INS_FCMP_CULE_S,
	LA_INS_FCMP_CULT_D,
	LA_INS_FCMP_CULT_S,
	LA_INS_FCMP_CUNE_D,
	LA_INS_FCMP_CUNE_S,
	LA_INS_FCMP_CUN_D,
	LA_INS_FCMP_CUN_S,
	LA_INS_FCMP_SAF_D,
	LA_INS_FCMP_SAF_S,
	LA_INS_FCMP_SEQ_D,
	LA_INS_FCMP_SEQ_S,
	LA_INS_FCMP_SGE_D,
	LA_INS_FCMP_SGE_S,
	LA_INS_FCMP_SGT_D,
	LA_INS_FCMP_SGT_S,
	LA_INS_FCMP_SLE_D,
	LA_INS_FCMP_SLE_S,
	LA_INS_FCMP_SLT_D,
	LA_INS_FCMP_SLT_S,
	LA_INS_FCMP_SNE_D,
	LA_INS_FCMP_SNE_S,
	LA_INS_FCMP_SOR_D,
	LA_INS_FCMP_SOR_S,
	LA_INS_FCMP_SUEQ_D,
	LA_INS_FCMP_SUEQ_S,
	LA_INS_FCMP_SULE_D,
	LA_INS_FCMP_SULE_S,
	LA_INS_FCMP_SULT_D,
	LA_INS_FCMP_SULT_S,
	LA_INS_FCMP_SUNE_D,
	LA_INS_FCMP_SUNE_S,
	LA_INS_FCMP_SUN_D,
	LA_INS_FCMP_SUN_S,
	LA_INS_FCOPYSIGN_D,
	LA_INS_FCOPYSIGN_S,
	LA_INS_FCVT_D_S,
	LA_INS_FCVT_S_D,
	LA_INS_FDIV_D,
	LA_INS_FDIV_S,
	LA_INS_FFINT_D_L,
	LA_INS_FFINT_D_W,
	LA_INS_FFINT_S_L,
	LA_INS_FFINT_S_W,
	LA_INS_FLDGT_D,
	LA_INS_FLDGT_S,
	LA_INS_FLDLE_D,
	LA_INS_FLDLE_S,
	LA_INS_FLDX_D,
	LA_INS_FLDX_S,
	LA_INS_FLD_D,
	LA_INS_FLD_S,
	LA_INS_FLOGB_D,
	LA_INS_FLOGB_S,
	LA_INS_FMADD_D,
	LA_INS_FMADD_S,
	LA_INS_FMAXA_D,
	LA_INS_FMAXA_S,
	LA_INS_FMAX_D,
	LA_INS_FMAX_S,
	LA_INS_FMINA_D,
	LA_INS_FMINA_S,
	LA_INS_FMIN_D,
	LA_INS_FMIN_S,
	LA_INS_FMOV_D,
	LA_INS_FMOV_S,
	LA_INS_FMSUB_D,
	LA_INS_FMSUB_S,
	LA_INS_FMUL_D,
	LA_INS_FMUL_S,
	LA_INS_FNEG_D,
	LA_INS_FNEG_S,
	LA_INS_FNMADD_D,
	LA_INS_FNMADD_S,
	LA_INS_FNMSUB_D,
	LA_INS_FNMSUB_S,
	LA_INS_FRECIP_D,
	LA_INS_FRECIP_S,
	LA_INS_FRINT_D,
	LA_INS_FRINT_S,
	LA_INS_FRSQRT_D,
	LA_INS_FRSQRT_S,
	LA_INS_FSCALEB_D,
	LA_INS_FSCALEB_S,
	LA_INS_FSEL,
	LA_INS_FSQRT_D,
	LA_INS_FSQRT_S,
	LA_INS_FSTGT_D,
	LA_INS_FSTGT_S,
	LA_INS_FSTLE_D,
	LA_INS_FSTLE_S,
	LA_INS_FSTX_D,
	LA_INS_FSTX_S,
	LA_INS_FST_D,
	LA_INS_FST_S,
	LA_INS_FSUB_D,
	LA_INS_FSUB_S,
	LA_INS_FTINTRM_L_D,
	LA_INS_FTINTRM_L_S,
	LA_INS_FTINTRM_W_D,
	LA_INS_FTINTRM_W_S,
	LA_INS_FTINTRNE_L_D,
	LA_INS_FTINTRNE_L_S,
	LA_INS_FTINTRNE_W_D,
	LA_INS_FTINTRNE_W_S,
	LA_INS_FTINTRP_L_D,
	LA_INS_FTINTRP_L_S,
	LA_INS_FTINTRP_W_D,
	LA_INS_FTINTRP_W_S,
	LA_INS_FTINTRZ_L_D,
	LA_INS_FTINTRZ_L_S,
	LA_INS_FTINTRZ_W_D,
	LA_INS_FTINTRZ_W_S,
	LA_INS_FTINT_L_D,
	LA_INS_FTINT_L_S,
	LA_INS_FTINT_W_D,
	LA_INS_FTINT_W_S,
	LA_INS_IBAR,
	LA_INS_IDLE,
	LA_INS_INVTLB,
	LA_INS_IOCSRRD_B,
	LA_INS_IOCSRRD_D,
	LA_INS_IOCSRRD_H,
	LA_INS_IOCSRRD_W,
	LA_INS_IOCSRWR_B,
	LA_INS_IOCSRWR_D,
	LA_INS_IOCSRWR_H,
	LA_INS_IOCSRWR_W,
	LA_INS_JIRL,
	LA_INS_LDDIR,
	LA_INS_LDGT_B,
	LA_INS_LDGT_D,
	LA_INS_LDGT_H,
	LA_INS_LDGT_W,
	LA_INS_LDLE_B,
	LA_INS_LDLE_D,
	LA_INS_LDLE_H,
	LA_INS_LDLE_W,
	LA_INS_LDPTE,
	LA_INS_LDPTR_D,
	LA_INS_LDPTR_W,
	LA_INS_LDX_B,
	LA_INS_LDX_BU,
	LA_INS_LDX_D,
	LA_INS_LDX_H,
	LA_INS_LDX_HU,
	LA_INS_LDX_W,
	LA_INS_LDX_WU,
	LA_INS_LD_B,
	LA_INS_LD_BU,
	LA_INS_LD_D,
	LA_INS_LD_H,
	LA_INS_LD_HU,
	LA_INS_LD_W,
	LA_INS_LD_WU,
	LA_INS_LL_D,
	LA_INS_LL_W,
	LA_INS_LU12I_W,
	LA_INS_LU32I_D,
	LA_INS_LU52I_D,
	LA_INS_MASKEQZ,
	LA_INS_MASKNEZ,
	LA_INS_MOD_D,
	LA_INS_MOD_DU,
	LA_INS_MOD_W,
	LA_INS_MOD_WU,
	LA_INS_MOVCF2FR,
	LA_INS_MOVCF2GR,
	LA_INS_MOVE,
	LA_INS_MOVFCSR2GR,
	LA_INS_MOVFR2CF,
	LA_INS_MOVFR2GR_D,
	LA_INS_MOVFR2GR_S,
	LA_INS_MOVFRH2GR_S,
	LA_INS_MOVGR2CF,
	LA_INS_MOVGR2FCSR,
	LA_INS_MOVGR2FRH_W,
	LA_INS_MOVGR2FR_D,
	LA_INS_MOVGR2FR_W,
	LA_INS_MULH_D,
	LA_INS_MULH_DU,
	LA_INS_MULH_W,
	LA_INS_MULH_WU,
	LA_INS_MULW_D_W,
	LA_INS_MULW_D_WU,
	LA_INS_MUL_D,
	LA_INS_MUL_W,
	LA_INS_NOR,
	LA_INS_OR,
	LA_INS_ORI,
	LA_INS_ORN,
	LA_INS_PCADDI,
	LA_INS_PCADDU12I,
	LA_INS_PCADDU18I,
	LA_INS_PCALAU12I,
	LA_INS_PRELD,
	LA_INS_PRELDX,
	LA_INS_RDTIMEH_W,
	LA_INS_RDTIMEL_W,
	LA_INS_RDTIME_D,
	LA_INS_REVB_2H,
	LA_INS_REVB_2W,
	LA_INS_REVB_4H,
	LA_INS_REVB_D,
	LA_INS_REVH_2W,
	LA_INS_REVH_D,
	LA_INS_ROTRI_D,
	LA_INS_ROTRI_W,
	LA_INS_ROTR_D,
	LA_INS_ROTR_W,
	LA_INS_SC_D,
	LA_INS_SC_W,
	LA_INS_SLLI_D,
	LA_INS_SLLI_W,
	LA_INS_SLL_D,
	LA_INS_SLL_W,
	LA_INS_SLT,
	LA_INS_SLTI,
	LA_INS_SLTU,
	LA_INS_SLTUI,
	LA_INS_SRAI_D,
	LA_INS_SRAI_W,
	LA_INS_SRA_D,
	LA_INS_SRA_W,
	LA_INS_SRLI_D,
	LA_INS_SRLI_W,
	LA_INS_SRL_D,
	LA_INS_SRL_W,
	LA_INS_STGT_B,
	LA_INS_STGT_D,
	LA_INS_STGT_H,
	LA_INS_STGT_W,
	LA_INS_STLE_B,
	LA_INS_STLE_D,
	LA_INS_STLE_H,
	LA_INS_STLE_W,
	LA_INS_STPTR_D,
	LA_INS_STPTR_W,
	LA_INS_STX_B,
	LA_INS_STX_D,
	LA_INS_STX_H,
	LA_INS_STX_W,
	LA_INS_ST_B,
	LA_INS_ST_D,
	LA_INS_ST_H,
	LA_INS_ST_W,
	LA_INS_SUB_D,
	LA_INS_SUB_W,
	LA_INS_SYSCALL,
	LA_INS_TLBCLR,
	LA_INS_TLBFILL,
	LA_INS_TLBFLUSH,
	LA_INS_TLBRD,
	LA_INS_TLBSRCH,
	LA_INS_TLBWR,
	LA_INS_XOR,
	LA_INS_XORI,

	LA_INS_ENDING,
} la_insn;

static const struct loongarch_anal_opcode la_lmm_opcodes[] = {
	{ 0x2000000, 0xffc00000, "slti", LA_INS_SLTI },
	{ 0x2400000, 0xffc00000, "sltui", LA_INS_SLTUI },
	{ 0x2800000, 0xffc00000, "addi.w", LA_INS_ADDI_W },
	{ 0x2c00000, 0xffc00000, "addi.d", LA_INS_ADDI_D },
	{ 0x3000000, 0xffc00000, "lu52i.d", LA_INS_LU52I_D },
	{ 0x3400000, 0xffc00000, "andi", LA_INS_ANDI, R_ANAL_OP_TYPE_AND },
	{ 0x3800000, 0xffc00000, "ori", LA_INS_ORI, R_ANAL_OP_TYPE_OR},
	{ 0x3c00000, 0xffc00000, "xori", LA_INS_XORI, R_ANAL_OP_TYPE_XOR },
	{ 0x10000000, 0xfc000000, "addu16i.d", LA_INS_ADDU16I_D, R_ANAL_OP_TYPE_ADD },
	{ 0x14000000, 0xfe000000, "lu12i.w", LA_INS_LU12I_W },
	{ 0x16000000, 0xfe000000, "lu32i.d", LA_INS_LU32I_D },
	{ 0x18000000, 0xfe000000, "pcaddi", LA_INS_PCADDI, R_ANAL_OP_TYPE_ADD},
	{ 0x1a000000, 0xfe000000, "pcalau12i", LA_INS_PCALAU12I, R_ANAL_OP_TYPE_ADD },
	{ 0x1c000000, 0xfe000000, "pcaddu12i", LA_INS_PCADDU12I, R_ANAL_OP_TYPE_ADD},
	{ 0x1e000000, 0xfe000000, "pcaddu18i", LA_INS_PCADDU18I, R_ANAL_OP_TYPE_ADD },
	{0}
};

static const struct loongarch_anal_opcode la_privilege_opcodes[] = {
	{ 0x4000000, 0xff0003e0, "csrrd", LA_INS_CSRRD },
	{ 0x4000020, 0xff0003e0, "csrwr", LA_INS_CSRWR },
	{ 0x4000000, 0xff000000, "csrxchg", LA_INS_CSRXCHG },
	{ 0x6000000, 0xffc00000, "cacop", LA_INS_CACOP },
	{ 0x6400000, 0xfffc0000, "lddir", LA_INS_LDDIR },
	{ 0x6440000, 0xfffc001f, "ldpte", LA_INS_LDPTE },
	{ 0x6480000, 0xfffffc00, "iocsrrd.b", LA_INS_IOCSRRD_B },
	{ 0x6480400, 0xfffffc00, "iocsrrd.h", LA_INS_IOCSRRD_H },
	{ 0x6480800, 0xfffffc00, "iocsrrd.w", LA_INS_IOCSRRD_W },
	{ 0x6480c00, 0xfffffc00, "iocsrrd.d", LA_INS_IOCSRRD_D },
	{ 0x6481000, 0xfffffc00, "iocsrwr.b", LA_INS_IOCSRWR_B },
	{ 0x6481400, 0xfffffc00, "iocsrwr.h", LA_INS_IOCSRWR_H },
	{ 0x6481800, 0xfffffc00, "iocsrwr.w", LA_INS_IOCSRWR_W },
	{ 0x6481c00, 0xfffffc00, "iocsrwr.d", LA_INS_IOCSRWR_D },
	{ 0x6482000, 0xffffffff, "tlbclr", LA_INS_TLBCLR },
	{ 0x6482400, 0xffffffff, "tlbflush", LA_INS_TLBFLUSH },
	{ 0x6482800, 0xffffffff, "tlbsrch", LA_INS_TLBSRCH },
	{ 0x6482c00, 0xffffffff, "tlbrd", LA_INS_TLBRD },
	{ 0x6483000, 0xffffffff, "tlbwr", LA_INS_TLBWR },
	{ 0x6483400, 0xffffffff, "tlbfill", LA_INS_TLBFILL },
	{ 0x6483800, 0xffffffff, "ertn", LA_INS_ERTN },
	{ 0x6488000, 0xffff8000, "idle", LA_INS_IDLE },
	{ 0x6498000, 0xffff8000, "invtlb", LA_INS_INVTLB },
	{0}
};

static const struct loongarch_anal_opcode la_jmp_opcodes[] = {
	{ 0x40000000, 0xfc000000, "beqz", LA_INS_BEQZ, R_ANAL_OP_TYPE_CJMP },
	{ 0x44000000, 0xfc000000, "bnez", LA_INS_BNEZ, R_ANAL_OP_TYPE_CJMP },
	{ 0x48000000, 0xfc000300, "bceqz", LA_INS_BCEQZ, R_ANAL_OP_TYPE_CJMP },
	{ 0x48000100, 0xfc000300, "bcnez", LA_INS_BCNEZ, R_ANAL_OP_TYPE_CJMP },
	{ 0x4c000000, 0xfc000000, "jirl", LA_INS_JIRL, R_ANAL_OP_TYPE_RCALL },
	{ 0x50000000, 0xfc000000, "b", LA_INS_B, R_ANAL_OP_TYPE_JMP },
	{ 0x54000000, 0xfc000000, "bl", LA_INS_BL, R_ANAL_OP_TYPE_CALL },
	{ 0x58000000, 0xfc000000, "beq", LA_INS_BEQ, R_ANAL_OP_TYPE_CJMP },
	{ 0x5c000000, 0xfc000000, "bne", LA_INS_BNE, R_ANAL_OP_TYPE_CJMP },
	{ 0x60000000, 0xfc000000, "blt", LA_INS_BLT, R_ANAL_OP_TYPE_CJMP },
	{ 0x60000000, 0xfc000000, "bgt", LA_INS_BGT, R_ANAL_OP_TYPE_CJMP },
	{ 0x60000000, 0xfc00001f, "bltz", LA_INS_BLTZ, R_ANAL_OP_TYPE_CJMP },
	{ 0x60000000, 0xfc0003e0, "bgtz", LA_INS_BGTZ, R_ANAL_OP_TYPE_CJMP },
	{ 0x64000000, 0xfc000000, "bge", LA_INS_BGE, R_ANAL_OP_TYPE_CJMP },
	{ 0x64000000, 0xfc000000, "ble", LA_INS_BLE, R_ANAL_OP_TYPE_CJMP },
	{ 0x64000000, 0xfc00001f, "bgez", LA_INS_BGEZ, R_ANAL_OP_TYPE_CJMP },
	{ 0x64000000, 0xfc0003e0, "blez", LA_INS_BLEZ, R_ANAL_OP_TYPE_CJMP },
	{ 0x68000000, 0xfc000000, "bltu", LA_INS_BLTU, R_ANAL_OP_TYPE_CJMP },
	{ 0x68000000, 0xfc000000, "bgtu", LA_INS_BGTU, R_ANAL_OP_TYPE_CJMP },
	{ 0x6c000000, 0xfc000000, "bgeu", LA_INS_BGEU, R_ANAL_OP_TYPE_CJMP },
	{ 0x6c000000, 0xfc000000, "bleu", LA_INS_BLEU, R_ANAL_OP_TYPE_CJMP },
	{0}
};

static struct loongarch_anal_opcode la_load_opcodes[] = {
	{ 0x20000000, 0xff000000, "ll.w", LA_INS_LL_W, R_ANAL_OP_TYPE_LOAD },
	{ 0x21000000, 0xff000000, "sc.w", LA_INS_SC_W, R_ANAL_OP_TYPE_STORE },
	{ 0x22000000, 0xff000000, "ll.d", LA_INS_LL_D, R_ANAL_OP_TYPE_LOAD },
	{ 0x23000000, 0xff000000, "sc.d", LA_INS_SC_D, R_ANAL_OP_TYPE_STORE },
	{ 0x24000000, 0xff000000, "ldptr.w", LA_INS_LDPTR_W, R_ANAL_OP_TYPE_LOAD },
	{ 0x25000000, 0xff000000, "stptr.w", LA_INS_STPTR_W, R_ANAL_OP_TYPE_STORE },
	{ 0x26000000, 0xff000000, "ldptr.d", LA_INS_LDPTR_D, R_ANAL_OP_TYPE_LOAD },
	{ 0x27000000, 0xff000000, "stptr.d", LA_INS_STPTR_D, R_ANAL_OP_TYPE_STORE },
	{ 0x28000000, 0xffc00000, "ld.b", LA_INS_LD_B, R_ANAL_OP_TYPE_LOAD },
	{ 0x28400000, 0xffc00000, "ld.h", LA_INS_LD_H, R_ANAL_OP_TYPE_LOAD },
	{ 0x28800000, 0xffc00000, "ld.w", LA_INS_LD_W, R_ANAL_OP_TYPE_LOAD },
	{ 0x28c00000, 0xffc00000, "ld.d", LA_INS_LD_D, R_ANAL_OP_TYPE_LOAD },
	{ 0x29000000, 0xffc00000, "st.b", LA_INS_ST_B, R_ANAL_OP_TYPE_LOAD },
	{ 0x29400000, 0xffc00000, "st.h", LA_INS_ST_H, R_ANAL_OP_TYPE_LOAD },
	{ 0x29800000, 0xffc00000, "st.w", LA_INS_ST_W, R_ANAL_OP_TYPE_LOAD },
	{ 0x29c00000, 0xffc00000, "st.d", LA_INS_ST_D, R_ANAL_OP_TYPE_LOAD },
	{ 0x2a000000, 0xffc00000, "ld.bu", LA_INS_LD_BU, R_ANAL_OP_TYPE_LOAD },
	{ 0x2a400000, 0xffc00000, "ld.hu", LA_INS_LD_HU, R_ANAL_OP_TYPE_LOAD },
	{ 0x2a800000, 0xffc00000, "ld.wu", LA_INS_LD_WU, R_ANAL_OP_TYPE_LOAD },
	{ 0x2ac00000, 0xffc00000, "preld", LA_INS_PRELD, R_ANAL_OP_TYPE_LOAD },
	{ 0x2b000000, 0xffc00000, "fld.s", LA_INS_FLD_S, R_ANAL_OP_TYPE_LOAD },
	{ 0x2b400000, 0xffc00000, "fst.s", LA_INS_FST_S, R_ANAL_OP_TYPE_STORE },
	{ 0x2b800000, 0xffc00000, "fld.d", LA_INS_FLD_D, R_ANAL_OP_TYPE_LOAD },
	{ 0x2bc00000, 0xffc00000, "fst.d", LA_INS_FST_D, R_ANAL_OP_TYPE_STORE },
	{ 0x38000000, 0xffff8000, "ldx.b", LA_INS_LDX_B, R_ANAL_OP_TYPE_LOAD },
	{ 0x38040000, 0xffff8000, "ldx.h", LA_INS_LDX_H, R_ANAL_OP_TYPE_LOAD },
	{ 0x38080000, 0xffff8000, "ldx.w", LA_INS_LDX_W, R_ANAL_OP_TYPE_LOAD },
	{ 0x380c0000, 0xffff8000, "ldx.d", LA_INS_LDX_D, R_ANAL_OP_TYPE_LOAD },
	{ 0x38100000, 0xffff8000, "stx.b", LA_INS_STX_B, R_ANAL_OP_TYPE_STORE },
	{ 0x38140000, 0xffff8000, "stx.h", LA_INS_STX_H, R_ANAL_OP_TYPE_STORE },
	{ 0x38180000, 0xffff8000, "stx.w", LA_INS_STX_W, R_ANAL_OP_TYPE_STORE },
	{ 0x381c0000, 0xffff8000, "stx.d", LA_INS_STX_D, R_ANAL_OP_TYPE_STORE },
	{ 0x38200000, 0xffff8000, "ldx.bu", LA_INS_LDX_BU, R_ANAL_OP_TYPE_LOAD },
	{ 0x38240000, 0xffff8000, "ldx.hu", LA_INS_LDX_HU, R_ANAL_OP_TYPE_LOAD },
	{ 0x38280000, 0xffff8000, "ldx.wu", LA_INS_LDX_WU, R_ANAL_OP_TYPE_LOAD },
	{ 0x382c0000, 0xffff8000, "preldx", LA_INS_PRELDX, R_ANAL_OP_TYPE_LOAD },
	{ 0x38300000, 0xffff8000, "fldx.s", LA_INS_FLDX_S, R_ANAL_OP_TYPE_LOAD },
	{ 0x38340000, 0xffff8000, "fldx.d", LA_INS_FLDX_D, R_ANAL_OP_TYPE_LOAD },
	{ 0x38380000, 0xffff8000, "fstx.s", LA_INS_FSTX_S, R_ANAL_OP_TYPE_STORE },
	{ 0x383c0000, 0xffff8000, "fstx.d", LA_INS_FSTX_D, R_ANAL_OP_TYPE_STORE },
	{ 0x38600000, 0xffff8000, "amswap.w", LA_INS_AMSWAP_W, R_ANAL_OP_TYPE_XCHG },
	{ 0x38608000, 0xffff8000, "amswap.d", LA_INS_AMSWAP_D, R_ANAL_OP_TYPE_XCHG },
	{ 0x38610000, 0xffff8000, "amadd.w", LA_INS_AMADD_W, R_ANAL_OP_TYPE_ADD },
	{ 0x38618000, 0xffff8000, "amadd.d", LA_INS_AMADD_D, R_ANAL_OP_TYPE_ADD },
	{ 0x38620000, 0xffff8000, "amand.w", LA_INS_AMAND_W },
	{ 0x38628000, 0xffff8000, "amand.d", LA_INS_AMAND_D },
	{ 0x38630000, 0xffff8000, "amor.w", LA_INS_AMOR_W },
	{ 0x38638000, 0xffff8000, "amor.d", LA_INS_AMOR_D },
	{ 0x38640000, 0xffff8000, "amxor.w", LA_INS_AMXOR_W },
	{ 0x38648000, 0xffff8000, "amxor.d", LA_INS_AMXOR_D },
	{ 0x38650000, 0xffff8000, "ammax.w", LA_INS_AMMAX_W },
	{ 0x38658000, 0xffff8000, "ammax.d", LA_INS_AMMAX_D },
	{ 0x38660000, 0xffff8000, "ammin.w", LA_INS_AMMIN_W },
	{ 0x38668000, 0xffff8000, "ammin.d", LA_INS_AMMIN_D },
	{ 0x38670000, 0xffff8000, "ammax.wu", LA_INS_AMMAX_WU },
	{ 0x38678000, 0xffff8000, "ammax.du", LA_INS_AMMAX_DU },
	{ 0x38680000, 0xffff8000, "ammin.wu", LA_INS_AMMIN_WU },
	{ 0x38688000, 0xffff8000, "ammin.du", LA_INS_AMMIN_DU },
	{ 0x38690000, 0xffff8000, "amswap_db.w", LA_INS_AMSWAP_DB_W, R_ANAL_OP_TYPE_XCHG },
	{ 0x38698000, 0xffff8000, "amswap_db.d", LA_INS_AMSWAP_DB_D, R_ANAL_OP_TYPE_XCHG },
	{ 0x386a0000, 0xffff8000, "amadd_db.w", LA_INS_AMADD_DB_W, R_ANAL_OP_TYPE_ADD },
	{ 0x386a8000, 0xffff8000, "amadd_db.d", LA_INS_AMADD_DB_D, R_ANAL_OP_TYPE_ADD },
	{ 0x386b0000, 0xffff8000, "amand_db.w", LA_INS_AMAND_DB_W },
	{ 0x386b8000, 0xffff8000, "amand_db.d", LA_INS_AMAND_DB_D },
	{ 0x386c0000, 0xffff8000, "amor_db.w", LA_INS_AMOR_DB_W },
	{ 0x386c8000, 0xffff8000, "amor_db.d", LA_INS_AMOR_DB_D },
	{ 0x386d0000, 0xffff8000, "amxor_db.w", LA_INS_AMXOR_DB_W },
	{ 0x386d8000, 0xffff8000, "amxor_db.d", LA_INS_AMXOR_DB_D },
	{ 0x386e0000, 0xffff8000, "ammax_db.w", LA_INS_AMMAX_DB_W },
	{ 0x386e8000, 0xffff8000, "ammax_db.d", LA_INS_AMMAX_DB_D },
	{ 0x386f0000, 0xffff8000, "ammin_db.w", LA_INS_AMMIN_DB_W },
	{ 0x386f8000, 0xffff8000, "ammin_db.d", LA_INS_AMMIN_DB_D },
	{ 0x38700000, 0xffff8000, "ammax_db.wu", LA_INS_AMMAX_DB_WU },
	{ 0x38708000, 0xffff8000, "ammax_db.du", LA_INS_AMMAX_DB_DU },
	{ 0x38710000, 0xffff8000, "ammin_db.wu", LA_INS_AMMIN_DB_WU },
	{ 0x38718000, 0xffff8000, "ammin_db.du", LA_INS_AMMIN_DB_DU },
	{ 0x38720000, 0xffff8000, "dbar", LA_INS_DBAR },
	{ 0x38728000, 0xffff8000, "ibar", LA_INS_IBAR },
	{ 0x38740000, 0xffff8000, "fldgt.s", LA_INS_FLDGT_S },
	{ 0x38748000, 0xffff8000, "fldgt.d", LA_INS_FLDGT_D },
	{ 0x38750000, 0xffff8000, "fldle.s", LA_INS_FLDLE_S },
	{ 0x38758000, 0xffff8000, "fldle.d", LA_INS_FLDLE_D },
	{ 0x38760000, 0xffff8000, "fstgt.s", LA_INS_FSTGT_S },
	{ 0x38768000, 0xffff8000, "fstgt.d", LA_INS_FSTGT_D },
	{ 0x38770000, 0xffff8000, "fstle.s", LA_INS_FSTLE_S },
	{ 0x38778000, 0xffff8000, "fstle.d", LA_INS_FSTLE_D },
	{ 0x38780000, 0xffff8000, "ldgt.b", LA_INS_LDGT_B },
	{ 0x38788000, 0xffff8000, "ldgt.h", LA_INS_LDGT_H },
	{ 0x38790000, 0xffff8000, "ldgt.w", LA_INS_LDGT_W },
	{ 0x38798000, 0xffff8000, "ldgt.d", LA_INS_LDGT_D },
	{ 0x387a0000, 0xffff8000, "ldle.b", LA_INS_LDLE_B },
	{ 0x387a8000, 0xffff8000, "ldle.h", LA_INS_LDLE_H },
	{ 0x387b0000, 0xffff8000, "ldle.w", LA_INS_LDLE_W },
	{ 0x387b8000, 0xffff8000, "ldle.d", LA_INS_LDLE_D },
	{ 0x387c0000, 0xffff8000, "stgt.b", LA_INS_STGT_B },
	{ 0x387c8000, 0xffff8000, "stgt.h", LA_INS_STGT_H },
	{ 0x387d0000, 0xffff8000, "stgt.w", LA_INS_STGT_W },
	{ 0x387d8000, 0xffff8000, "stgt.d", LA_INS_STGT_D },
	{ 0x387e0000, 0xffff8000, "stle.b", LA_INS_STLE_B },
	{ 0x387e8000, 0xffff8000, "stle.h", LA_INS_STLE_H },
	{ 0x387f0000, 0xffff8000, "stle.w", LA_INS_STLE_W },
	{ 0x387f8000, 0xffff8000, "stle.d", LA_INS_STLE_D },
	{0}
};

static struct loongarch_anal_opcode la_fix_opcodes[] = {
	{ 0x1000, 0xfffffc00, "clo.w", LA_INS_CLO_W },
	{ 0x1400, 0xfffffc00, "clz.w", LA_INS_CLZ_W },
	{ 0x1800, 0xfffffc00, "cto.w", LA_INS_CTO_W },
	{ 0x1c00, 0xfffffc00, "ctz.w", LA_INS_CTZ_W },
	{ 0x2000, 0xfffffc00, "clo.d", LA_INS_CLO_D },
	{ 0x2400, 0xfffffc00, "clz.d", LA_INS_CLZ_D },
	{ 0x2800, 0xfffffc00, "cto.d", LA_INS_CTO_D },
	{ 0x2c00, 0xfffffc00, "ctz.d", LA_INS_CTZ_D },
	{ 0x3000, 0xfffffc00, "revb.2h", LA_INS_REVB_2H },
	{ 0x3400, 0xfffffc00, "revb.4h", LA_INS_REVB_4H },
	{ 0x3800, 0xfffffc00, "revb.2w", LA_INS_REVB_2W },
	{ 0x3c00, 0xfffffc00, "revb.d", LA_INS_REVB_D },
	{ 0x4000, 0xfffffc00, "revh.2w", LA_INS_REVH_2W },
	{ 0x4400, 0xfffffc00, "revh.d", LA_INS_REVH_D },
	{ 0x4800, 0xfffffc00, "bitrev.4b", LA_INS_BITREV_4B },
	{ 0x4c00, 0xfffffc00, "bitrev.8b", LA_INS_BITREV_8B },
	{ 0x5000, 0xfffffc00, "bitrev.w", LA_INS_BITREV_W },
	{ 0x5400, 0xfffffc00, "bitrev.d", LA_INS_BITREV_D },
	{ 0x5800, 0xfffffc00, "ext.w.h", LA_INS_EXT_W_H },
	{ 0x5c00, 0xfffffc00, "ext.w.b", LA_INS_EXT_W_B },
	{ 0x6000, 0xfffffc00, "rdtimel.w", LA_INS_RDTIMEL_W },
	{ 0x6400, 0xfffffc00, "rdtimeh.w", LA_INS_RDTIMEH_W },
	{ 0x6800, 0xfffffc00, "rdtime.d", LA_INS_RDTIME_D },
	{ 0x6c00, 0xfffffc00, "cpucfg", LA_INS_CPUCFG },
	{ 0x10000, 0xffff801f, "asrtle.d", LA_INS_ASRTLE_D },
	{ 0x18000, 0xffff801f, "asrtgt.d", LA_INS_ASRTGT_D },
	{ 0x40000, 0xfffe0000, "alsl.w", LA_INS_ALSL_W },
	{ 0x60000, 0xfffe0000, "alsl.wu", LA_INS_ALSL_WU },
	{ 0x80000, 0xfffe0000, "bytepick.w", LA_INS_BYTEPICK_W },
	{ 0xc0000, 0xfffc0000, "bytepick.d", LA_INS_BYTEPICK_D },
	{ 0x100000, 0xffff8000, "add.w", LA_INS_ADD_W, R_ANAL_OP_TYPE_ADD },
	{ 0x108000, 0xffff8000, "add.d", LA_INS_ADD_D, R_ANAL_OP_TYPE_ADD },
	{ 0x110000, 0xffff8000, "sub.w", LA_INS_SUB_W, R_ANAL_OP_TYPE_SUB },
	{ 0x118000, 0xffff8000, "sub.d", LA_INS_SUB_D, R_ANAL_OP_TYPE_SUB },
	{ 0x120000, 0xffff8000, "slt", LA_INS_SLT },
	{ 0x128000, 0xffff8000, "sltu", LA_INS_SLTU },
	{ 0x130000, 0xffff8000, "maskeqz", LA_INS_MASKEQZ },
	{ 0x138000, 0xffff8000, "masknez", LA_INS_MASKNEZ },
	{ 0x140000, 0xffff8000, "nor", LA_INS_NOR, R_ANAL_OP_TYPE_NOR },
	{ 0x148000, 0xffff8000, "and", LA_INS_AND, R_ANAL_OP_TYPE_AND },
	{ 0x150000, 0xfffffc00, "move", LA_INS_MOVE, R_ANAL_OP_TYPE_MOV },
	{ 0x150000, 0xffff8000, "or", LA_INS_OR, R_ANAL_OP_TYPE_OR },
	{ 0x158000, 0xffff8000, "xor", LA_INS_XOR, R_ANAL_OP_TYPE_XOR },
	{ 0x160000, 0xffff8000, "orn", LA_INS_ORN },
	{ 0x168000, 0xffff8000, "andn", LA_INS_ANDN },
	{ 0x170000, 0xffff8000, "sll.w", LA_INS_SLL_W, R_ANAL_OP_TYPE_SHL },
	{ 0x178000, 0xffff8000, "srl.w", LA_INS_SRL_W, R_ANAL_OP_TYPE_SHR },
	{ 0x180000, 0xffff8000, "sra.w", LA_INS_SRA_W, R_ANAL_OP_TYPE_SHR },
	{ 0x188000, 0xffff8000, "sll.d", LA_INS_SLL_D, R_ANAL_OP_TYPE_SHL },
	{ 0x190000, 0xffff8000, "srl.d", LA_INS_SRL_D, R_ANAL_OP_TYPE_SHL },
	{ 0x198000, 0xffff8000, "sra.d", LA_INS_SRA_D, R_ANAL_OP_TYPE_SHR },
	{ 0x1b0000, 0xffff8000, "rotr.w", LA_INS_ROTR_W, R_ANAL_OP_TYPE_ROR },
	{ 0x1b8000, 0xffff8000, "rotr.d", LA_INS_ROTR_D, R_ANAL_OP_TYPE_ROR },
	{ 0x1c0000, 0xffff8000, "mul.w", LA_INS_MUL_W, R_ANAL_OP_TYPE_MUL },
	{ 0x1c8000, 0xffff8000, "mulh.w", LA_INS_MULH_W, R_ANAL_OP_TYPE_MUL },
	{ 0x1d0000, 0xffff8000, "mulh.wu", LA_INS_MULH_WU, R_ANAL_OP_TYPE_MUL },
	{ 0x1d8000, 0xffff8000, "mul.d", LA_INS_MUL_D, R_ANAL_OP_TYPE_MUL },
	{ 0x1e0000, 0xffff8000, "mulh.d", LA_INS_MULH_D, R_ANAL_OP_TYPE_MUL },
	{ 0x1e8000, 0xffff8000, "mulh.du", LA_INS_MULH_DU, R_ANAL_OP_TYPE_MUL },
	{ 0x1f0000, 0xffff8000, "mulw.d.w", LA_INS_MULW_D_W, R_ANAL_OP_TYPE_MUL },
	{ 0x1f8000, 0xffff8000, "mulw.d.wu", LA_INS_MULW_D_WU, R_ANAL_OP_TYPE_MUL },
	{ 0x200000, 0xffff8000, "div.w", LA_INS_DIV_W, R_ANAL_OP_TYPE_DIV },
	{ 0x208000, 0xffff8000, "mod.w", LA_INS_MOD_W, R_ANAL_OP_TYPE_MOD },
	{ 0x210000, 0xffff8000, "div.wu", LA_INS_DIV_WU, R_ANAL_OP_TYPE_DIV },
	{ 0x218000, 0xffff8000, "mod.wu", LA_INS_MOD_WU, R_ANAL_OP_TYPE_MOD },
	{ 0x220000, 0xffff8000, "div.d", LA_INS_DIV_D, R_ANAL_OP_TYPE_DIV },
	{ 0x228000, 0xffff8000, "mod.d", LA_INS_MOD_D, R_ANAL_OP_TYPE_MOD },
	{ 0x230000, 0xffff8000, "div.du", LA_INS_DIV_DU, R_ANAL_OP_TYPE_DIV },
	{ 0x238000, 0xffff8000, "mod.du", LA_INS_MOD_DU, R_ANAL_OP_TYPE_MOD },
	{ 0x240000, 0xffff8000, "crc.w.b.w", LA_INS_CRC_W_B_W },
	{ 0x248000, 0xffff8000, "crc.w.h.w", LA_INS_CRC_W_H_W },
	{ 0x250000, 0xffff8000, "crc.w.w.w", LA_INS_CRC_W_W_W },
	{ 0x258000, 0xffff8000, "crc.w.d.w", LA_INS_CRC_W_D_W },
	{ 0x260000, 0xffff8000, "crcc.w.b.w", LA_INS_CRCC_W_B_W },
	{ 0x268000, 0xffff8000, "crcc.w.h.w", LA_INS_CRCC_W_H_W },
	{ 0x270000, 0xffff8000, "crcc.w.w.w", LA_INS_CRCC_W_W_W },
	{ 0x278000, 0xffff8000, "crcc.w.d.w", LA_INS_CRCC_W_D_W },
	{ 0x2a0000, 0xffff8000, "break", LA_INS_BREAK },
	{ 0x2a8000, 0xffff8000, "dbcl", LA_INS_DBCL },
	{ 0x2b0000, 0xffff8000, "syscall", LA_INS_SYSCALL },
	{ 0x2c0000, 0xfffe0000, "alsl.d", LA_INS_ALSL_D , R_ANAL_OP_TYPE_SHL},
	{ 0x408000, 0xffff8000, "slli.w", LA_INS_SLLI_W, R_ANAL_OP_TYPE_SHL },
	{ 0x410000, 0xffff0000, "slli.d", LA_INS_SLLI_D, R_ANAL_OP_TYPE_SHL },
	{ 0x448000, 0xffff8000, "srli.w", LA_INS_SRLI_W, R_ANAL_OP_TYPE_SHL },
	{ 0x450000, 0xffff0000, "srli.d", LA_INS_SRLI_D, R_ANAL_OP_TYPE_SHL },
	{ 0x488000, 0xffff8000, "srai.w", LA_INS_SRAI_W, R_ANAL_OP_TYPE_SHR },
	{ 0x490000, 0xffff0000, "srai.d", LA_INS_SRAI_D, R_ANAL_OP_TYPE_SHR },
	{ 0x4c8000, 0xffff8000, "rotri.w", LA_INS_ROTRI_W, R_ANAL_OP_TYPE_ROR },
	{ 0x4d0000, 0xffff0000, "rotri.d", LA_INS_ROTRI_D, R_ANAL_OP_TYPE_ROR },
	{ 0x600000, 0xffe08000, "bstrins.w", LA_INS_BSTRINS_W },
	{ 0x608000, 0xffe08000, "bstrpick.w", LA_INS_BSTRPICK_W },
	{ 0x800000, 0xffc00000, "bstrins.d", LA_INS_BSTRINS_D },
	{ 0xc00000, 0xffc00000, "bstrpick.d", LA_INS_BSTRPICK_D },
	{0}
};
//TODO add float type
static struct loongarch_anal_opcode la_4opt_opcodes[] = {
	{ 0x8100000, 0xfff00000, "fmadd.s", LA_INS_FMADD_S },
	{ 0x8200000, 0xfff00000, "fmadd.d", LA_INS_FMADD_D },
	{ 0x8500000, 0xfff00000, "fmsub.s", LA_INS_FMSUB_S },
	{ 0x8900000, 0xfff00000, "fnmadd.s", LA_INS_FNMADD_S },
	{ 0x8a00000, 0xfff00000, "fnmadd.d", LA_INS_FNMADD_D },
	{ 0x8d00000, 0xfff00000, "fnmsub.s", LA_INS_FNMSUB_S },
	{ 0x8e00000, 0xfff00000, "fnmsub.d", LA_INS_FNMSUB_D },
	{ 0xc100000, 0xffff8018, "fcmp.caf.s", LA_INS_FCMP_CAF_S },
	{ 0xc108000, 0xffff8018, "fcmp.saf.s", LA_INS_FCMP_SAF_S },
	{ 0xc110000, 0xffff8018, "fcmp.clt.s", LA_INS_FCMP_CLT_S },
	{ 0xc118000, 0xffff8018, "fcmp.slt.s", LA_INS_FCMP_SLT_S },
	{ 0xc118000, 0xffff8018, "fcmp.sgt.s", LA_INS_FCMP_SGT_S },
	{ 0xc120000, 0xffff8018, "fcmp.ceq.s", LA_INS_FCMP_CEQ_S },
	{ 0xc128000, 0xffff8018, "fcmp.seq.s", LA_INS_FCMP_SEQ_S },
	{ 0xc130000, 0xffff8018, "fcmp.cle.s", LA_INS_FCMP_CLE_S },
	{ 0xc138000, 0xffff8018, "fcmp.sle.s", LA_INS_FCMP_SLE_S },
	{ 0xc138000, 0xffff8018, "fcmp.sge.s", LA_INS_FCMP_SGE_S },
	{ 0xc140000, 0xffff8018, "fcmp.cun.s", LA_INS_FCMP_CUN_S },
	{ 0xc148000, 0xffff8018, "fcmp.sun.s", LA_INS_FCMP_SUN_S },
	{ 0xc150000, 0xffff8018, "fcmp.cult.s", LA_INS_FCMP_CULT_S },
	{ 0xc150000, 0xffff8018, "fcmp.cugt.s", LA_INS_FCMP_CUGT_S },
	{ 0xc158000, 0xffff8018, "fcmp.sult.s", LA_INS_FCMP_SULT_S },
	{ 0xc160000, 0xffff8018, "fcmp.cueq.s", LA_INS_FCMP_CUEQ_S },
	{ 0xc168000, 0xffff8018, "fcmp.sueq.s", LA_INS_FCMP_SUEQ_S },
	{ 0xc170000, 0xffff8018, "fcmp.cule.s", LA_INS_FCMP_CULE_S },
	{ 0xc170000, 0xffff8018, "fcmp.cuge.s", LA_INS_FCMP_CUGE_S },
	{ 0xc178000, 0xffff8018, "fcmp.sule.s", LA_INS_FCMP_SULE_S },
	{ 0xc180000, 0xffff8018, "fcmp.cne.s", LA_INS_FCMP_CNE_S },
	{ 0xc188000, 0xffff8018, "fcmp.sne.s", LA_INS_FCMP_SNE_S },
	{ 0xc1a0000, 0xffff8018, "fcmp.cor.s", LA_INS_FCMP_COR_S },
	{ 0xc1a8000, 0xffff8018, "fcmp.sor.s", LA_INS_FCMP_SOR_S },
	{ 0xc1c0000, 0xffff8018, "fcmp.cune.s", LA_INS_FCMP_CUNE_S },
	{ 0xc1c8000, 0xffff8018, "fcmp.sune.s", LA_INS_FCMP_SUNE_S },
	{ 0xc200000, 0xffff8018, "fcmp.caf.d", LA_INS_FCMP_CAF_D },
	{ 0xc208000, 0xffff8018, "fcmp.saf.d", LA_INS_FCMP_SAF_D },
	{ 0xc210000, 0xffff8018, "fcmp.clt.d", LA_INS_FCMP_CLT_D },
	{ 0xc218000, 0xffff8018, "fcmp.slt.d", LA_INS_FCMP_SLT_D },
	{ 0xc218000, 0xffff8018, "fcmp.sgt.d", LA_INS_FCMP_SGT_D },
	{ 0xc220000, 0xffff8018, "fcmp.ceq.d", LA_INS_FCMP_CEQ_D },
	{ 0xc228000, 0xffff8018, "fcmp.seq.d", LA_INS_FCMP_SEQ_D },
	{ 0xc230000, 0xffff8018, "fcmp.cle.d", LA_INS_FCMP_CLE_D },
	{ 0xc238000, 0xffff8018, "fcmp.sle.d", LA_INS_FCMP_SLE_D },
	{ 0xc238000, 0xffff8018, "fcmp.sge.d", LA_INS_FCMP_SGE_D },
	{ 0xc240000, 0xffff8018, "fcmp.cun.d", LA_INS_FCMP_CUN_D },
	{ 0xc248000, 0xffff8018, "fcmp.sun.d", LA_INS_FCMP_SUN_D },
	{ 0xc250000, 0xffff8018, "fcmp.cult.d", LA_INS_FCMP_CULT_D },
	{ 0xc250000, 0xffff8018, "fcmp.cugt.d", LA_INS_FCMP_CUGT_D },
	{ 0xc258000, 0xffff8018, "fcmp.sult.d", LA_INS_FCMP_SULT_D },
	{ 0xc260000, 0xffff8018, "fcmp.cueq.d", LA_INS_FCMP_CUEQ_D },
	{ 0xc268000, 0xffff8018, "fcmp.sueq.d", LA_INS_FCMP_SUEQ_D },
	{ 0xc270000, 0xffff8018, "fcmp.cule.d", LA_INS_FCMP_CULE_D },
	{ 0xc270000, 0xffff8018, "fcmp.cuge.d", LA_INS_FCMP_CUGE_D },
	{ 0xc278000, 0xffff8018, "fcmp.sule.d", LA_INS_FCMP_SULE_D },
	{ 0xc280000, 0xffff8018, "fcmp.cne.d", LA_INS_FCMP_CNE_D },
	{ 0xc288000, 0xffff8018, "fcmp.sne.d", LA_INS_FCMP_SNE_D },
	{ 0xc2a0000, 0xffff8018, "fcmp.cor.d", LA_INS_FCMP_COR_D },
	{ 0xc2a8000, 0xffff8018, "fcmp.sor.d", LA_INS_FCMP_SOR_D },
	{ 0xc2c0000, 0xffff8018, "fcmp.cune.d", LA_INS_FCMP_CUNE_D },
	{ 0xc2c8000, 0xffff8018, "fcmp.sune.d", LA_INS_FCMP_SUNE_D },
	{ 0xd000000, 0xfffc0000, "fsel", LA_INS_FSEL },
	{0}
};

static struct loongarch_anal_opcode la_float_opcodes[] = {
	{ 0x1008000, 0xffff8000, "fadd.s", LA_INS_FADD_S },
	{ 0x1010000, 0xffff8000, "fadd.d", LA_INS_FADD_D },
	{ 0x1028000, 0xffff8000, "fsub.s", LA_INS_FSUB_S },
	{ 0x1030000, 0xffff8000, "fsub.d", LA_INS_FSUB_D },
	{ 0x1048000, 0xffff8000, "fmul.s", LA_INS_FMUL_S },
	{ 0x1050000, 0xffff8000, "fmul.d", LA_INS_FMUL_D },
	{ 0x1068000, 0xffff8000, "fdiv.s", LA_INS_FDIV_S },
	{ 0x1070000, 0xffff8000, "fdiv.d", LA_INS_FDIV_D },
	{ 0x1088000, 0xffff8000, "fmax.s", LA_INS_FMAX_S },
	{ 0x1090000, 0xffff8000, "fmax.d", LA_INS_FMAX_D },
	{ 0x10a8000, 0xffff8000, "fmin.s", LA_INS_FMIN_S },
	{ 0x10b0000, 0xffff8000, "fmin.d", LA_INS_FMIN_D },
	{ 0x10c8000, 0xffff8000, "fmaxa.s", LA_INS_FMAXA_S },
	{ 0x10d0000, 0xffff8000, "fmaxa.d", LA_INS_FMAXA_D },
	{ 0x10e8000, 0xffff8000, "fmina.s", LA_INS_FMINA_S },
	{ 0x10f0000, 0xffff8000, "fmina.d", LA_INS_FMINA_D },
	{ 0x1108000, 0xffff8000, "fscaleb.s", LA_INS_FSCALEB_S },
	{ 0x1110000, 0xffff8000, "fscaleb.d", LA_INS_FSCALEB_D },
	{ 0x1128000, 0xffff8000, "fcopysign.s", LA_INS_FCOPYSIGN_S },
	{ 0x1130000, 0xffff8000, "fcopysign.d", LA_INS_FCOPYSIGN_D },
	{ 0x1140400, 0xfffffc00, "fabs.s", LA_INS_FABS_S },
	{ 0x1140800, 0xfffffc00, "fabs.d", LA_INS_FABS_D },
	{ 0x1141400, 0xfffffc00, "fneg.s", LA_INS_FNEG_S },
	{ 0x1141800, 0xfffffc00, "fneg.d", LA_INS_FNEG_D },
	{ 0x1142400, 0xfffffc00, "flogb.s", LA_INS_FLOGB_S },
	{ 0x1142800, 0xfffffc00, "flogb.d", LA_INS_FLOGB_D },
	{ 0x1143400, 0xfffffc00, "fclass.s", LA_INS_FCLASS_S },
	{ 0x1143800, 0xfffffc00, "fclass.d", LA_INS_FCLASS_D },
	{ 0x1144400, 0xfffffc00, "fsqrt.s", LA_INS_FSQRT_S },
	{ 0x1144800, 0xfffffc00, "fsqrt.d", LA_INS_FSQRT_D },
	{ 0x1145400, 0xfffffc00, "frecip.s", LA_INS_FRECIP_S },
	{ 0x1145800, 0xfffffc00, "frecip.d", LA_INS_FRECIP_D },
	{ 0x1146400, 0xfffffc00, "frsqrt.s", LA_INS_FRSQRT_S },
	{ 0x1146800, 0xfffffc00, "frsqrt.d", LA_INS_FRSQRT_D },
	{ 0x1149400, 0xfffffc00, "fmov.s", LA_INS_FMOV_S },
	{ 0x1149800, 0xfffffc00, "fmov.d", LA_INS_FMOV_D },
	{ 0x114a400, 0xfffffc00, "movgr2fr.w", LA_INS_MOVGR2FR_W },
	{ 0x114a800, 0xfffffc00, "movgr2fr.d", LA_INS_MOVGR2FR_D },
	{ 0x114ac00, 0xfffffc00, "movgr2frh.w", LA_INS_MOVGR2FRH_W },
	{ 0x114b400, 0xfffffc00, "movfr2gr.s", LA_INS_MOVFR2GR_S },
	{ 0x114b800, 0xfffffc00, "movfr2gr.d", LA_INS_MOVFR2GR_D },
	{ 0x114bc00, 0xfffffc00, "movfrh2gr.s", LA_INS_MOVFRH2GR_S },
	{ 0x114c000, 0xfffffc00, "movgr2fcsr", LA_INS_MOVGR2FCSR },
	{ 0x114c800, 0xfffffc00, "movfcsr2gr", LA_INS_MOVFCSR2GR },
	{ 0x114d000, 0xfffffc18, "movfr2cf", LA_INS_MOVFR2CF },
	{ 0x114d400, 0xffffff00, "movcf2fr", LA_INS_MOVCF2FR },
	{ 0x114d800, 0xfffffc18, "movgr2cf", LA_INS_MOVGR2CF },
	{ 0x114dc00, 0xffffff00, "movcf2gr", LA_INS_MOVCF2GR },
	{ 0x11a0400, 0xfffffc00, "ftintrm.w.s", LA_INS_FTINTRM_W_S },
	{ 0x11a0800, 0xfffffc00, "ftintrm.w.d", LA_INS_FTINTRM_W_D },
	{ 0x11a2400, 0xfffffc00, "ftintrm.l.s", LA_INS_FTINTRM_L_S },
	{ 0x11a2800, 0xfffffc00, "ftintrm.l.d", LA_INS_FTINTRM_L_D },
	{ 0x11a4400, 0xfffffc00, "ftintrp.w.s", LA_INS_FTINTRP_W_S },
	{ 0x11a4800, 0xfffffc00, "ftintrp.w.d", LA_INS_FTINTRP_W_D },
	{ 0x11a6400, 0xfffffc00, "ftintrp.l.s", LA_INS_FTINTRP_L_S },
	{ 0x11a6800, 0xfffffc00, "ftintrp.l.d", LA_INS_FTINTRP_L_D },
	{ 0x11a8400, 0xfffffc00, "ftintrz.w.s", LA_INS_FTINTRZ_W_S },
	{ 0x11a8800, 0xfffffc00, "ftintrz.w.d", LA_INS_FTINTRZ_W_D },
	{ 0x11aa400, 0xfffffc00, "ftintrz.l.s", LA_INS_FTINTRZ_L_S },
	{ 0x11aa800, 0xfffffc00, "ftintrz.l.d", LA_INS_FTINTRZ_L_D },
	{ 0x11ac400, 0xfffffc00, "ftintrne.w.s", LA_INS_FTINTRNE_W_S },
	{ 0x11ac800, 0xfffffc00, "ftintrne.w.d", LA_INS_FTINTRNE_W_D },
	{ 0x11ae400, 0xfffffc00, "ftintrne.l.s", LA_INS_FTINTRNE_L_S },
	{ 0x11ae800, 0xfffffc00, "ftintrne.l.d", LA_INS_FTINTRNE_L_D },
	{ 0x11b0400, 0xfffffc00, "ftint.w.s", LA_INS_FTINT_W_S },
	{ 0x11b0800, 0xfffffc00, "ftint.w.d", LA_INS_FTINT_W_D },
	{ 0x11b2400, 0xfffffc00, "ftint.l.s", LA_INS_FTINT_L_S },
	{ 0x11b2800, 0xfffffc00, "ftint.l.d", LA_INS_FTINT_L_D },
	{ 0x11d1000, 0xfffffc00, "ffint.s.w", LA_INS_FFINT_S_W },
	{ 0x11d1800, 0xfffffc00, "ffint.s.l", LA_INS_FFINT_S_L },
	{ 0x11d2000, 0xfffffc00, "ffint.d.w", LA_INS_FFINT_D_W },
	{ 0x11d2800, 0xfffffc00, "ffint.d.l", LA_INS_FFINT_D_L },
	{ 0x11e4400, 0xfffffc00, "frint.s", LA_INS_FRINT_S },
	{ 0x11e4800, 0xfffffc00, "frint.d", LA_INS_FRINT_D },
	{0}
};

static const struct loongarch_ASE la_ases_initial[] = {
	{la_lmm_opcodes, {0}, 0},
	{la_privilege_opcodes, {0}, 0},
	{la_jmp_opcodes, {0}, 0},
	{la_load_opcodes, {0}, 0},
	{la_fix_opcodes, {0}, 0},
	{la_4opt_opcodes, {0}, 0},
	{la_float_opcodes, {0}, 0},
	{0}
};

static int analop_esil(RArchSession *as, RAnalOp *op, ut32 opcode) {
	ut32 insn_id = op->id;

	switch (insn_id) {
	case LA_INS_PCADDU12I:
		r_strbuf_appendf (&op->esil, "0x%"LA_PFM",0x%x,+,%s,=", op->addr, I_I20(opcode), LA_RD());
		break;
	case LA_INS_LU12I_W:
		r_strbuf_appendf (&op->esil, "%d,12,<<,%s,=", I_I20(opcode), LA_RD());
		break;
	case LA_INS_LU32I_D:
		r_strbuf_appendf (&op->esil, "32,0x%x,<<,%s,0xffffffff,&,+,%s,=", I_I20(opcode), LA_RD(), LA_RD());
		break;
	case LA_INS_LU52I_D:
		r_strbuf_appendf (&op->esil, "52,0x%x,<<,%s,+,%s,=", I_I12(opcode), LA_RD(), LA_RD());
		break;
		/* FIXME U means unsigned comparison*/
	case LA_INS_LDX_BU:
	case LA_INS_LDX_B:
		r_strbuf_appendf (&op->esil, "%s,%s,+,[1],%s,=", LA_RJ(), LA_RK(), LA_RD());
		break;
	case LA_INS_LDX_HU:
	case LA_INS_LDX_H:
		r_strbuf_appendf (&op->esil, "%s,%s,+,[2],%s,=", LA_RJ(), LA_RK(), LA_RD());
		break;
	case LA_INS_LDX_WU:
	case LA_INS_LDX_W:
		r_strbuf_appendf (&op->esil, "%s,%s,+,[4],%s,=", LA_RJ(), LA_RK(), LA_RD());
		break;
	case LA_INS_LDX_D:
		r_strbuf_appendf (&op->esil, "%s,%s,+,[8],%s,=", LA_RJ(), LA_RK(), LA_RD());
		break;
	case LA_INS_LD_BU:
	case LA_INS_LD_B:
		r_strbuf_appendf (&op->esil, "%s,0x%"LA_PFM",+,[1],%s,=", LA_RJ(),I12_SX(opcode), LA_RD());
		break;
	case LA_INS_LD_HU:
	case LA_INS_LD_H:
		r_strbuf_appendf (&op->esil, "%s,0x%"LA_PFM",+,[2],%s,=", LA_RJ(),I12_SX(opcode), LA_RD());
		break;
	case LA_INS_LD_WU:
	case LA_INS_LD_W:
		r_strbuf_appendf (&op->esil, "%s,0x%"LA_PFM",+,[4],%s,=", LA_RJ(),I12_SX(opcode), LA_RD());
		break;
	case LA_INS_LD_D:
		r_strbuf_appendf (&op->esil, "%s,0x%"LA_PFM",+,[8],%s,=", LA_RJ(),I12_SX(opcode), LA_RD());
		break;
	case LA_INS_LDPTR_W:
		r_strbuf_appendf (&op->esil, "%s,0x%"LA_PFM",+,[4],%s,=", LA_RJ(), I14s2_SX(opcode), LA_RD());
		break;
	case LA_INS_LDPTR_D:
		r_strbuf_appendf (&op->esil,"%s,0x%"LA_PFM",+,[8],%s,=", LA_RJ(), I14s2_SX(opcode), LA_RD());
		break;
	case LA_INS_ST_B:
		r_strbuf_appendf (&op->esil, "%s,%s,0x%"LA_PFM",+,=[1]", LA_RD(), LA_RJ(), I12_SX(opcode));
		break;
	case LA_INS_ST_H:
		r_strbuf_appendf (&op->esil, "%s,%s,0x%"LA_PFM",+,=[2]", LA_RD(), LA_RJ(), I12_SX(opcode));
		break;
	case LA_INS_ST_W:
		r_strbuf_appendf (&op->esil, "%s,%s,0x%"LA_PFM",+,=[4]", LA_RD(), LA_RJ(), I12_SX(opcode));
		break;
	case LA_INS_ST_D:
		r_strbuf_appendf (&op->esil, "%s,%s,0x%"LA_PFM",+,=[8]", LA_RD(), LA_RJ(), I12_SX(opcode));
		break;
	case LA_INS_STPTR_W:
		r_strbuf_appendf (&op->esil, "%s,%s,0x%"LA_PFM",+,=[8]", LA_RD(), LA_RJ(), I14s2_SX(opcode));
		break;
	case LA_INS_STPTR_D:
		r_strbuf_appendf (&op->esil, "%s,%s,0x%"LA_PFM",+,=[8]", LA_RD(), LA_RJ(), I14s2_SX(opcode));
		break;
	case LA_INS_SLTU:
	case LA_INS_SLT:
		r_strbuf_appendf (&op->esil, "0,%s,=,%s,%s,<,?{,1,%s,=,}", LA_RD(), LA_RK(), LA_RJ(),LA_RD());
		break;
	case LA_INS_SLTUI:
	case LA_INS_SLTI:
		r_strbuf_appendf (&op->esil, "0,%s,=,0x%"LA_PFM",%s,<,?{,1,%s,=,}", LA_RD(), I12_SX(opcode), LA_RJ(),LA_RD());
		break;
		//FIXME maybe Lack of signed expansion
	case LA_INS_ADD_W:
		r_strbuf_appendf (&op->esil, ES_SX32("%s,%s,+")",%s,=", LA_RJ(), LA_RK(), LA_RD());
		break;
	case LA_INS_ADD_D:
		r_strbuf_appendf (&op->esil, "%s,%s,+,%s,=", LA_RJ(), LA_RK(), LA_RD());
		break;
	case LA_INS_ADDI_W:
		r_strbuf_appendf (&op->esil, ES_SX32("%s,0x%"LA_PFM",+")",%s,=", LA_RJ(), I12_SX(opcode), LA_RD());
		break;
	case LA_INS_ADDU16I_D:
		r_strbuf_appendf (&op->esil, "16,0x%"LA_PFM",<<,%s,+,%s,=", I16_SX(opcode), LA_RJ(), LA_RD());
		break;
	case LA_INS_SUB_W:
		r_strbuf_appendf (&op->esil, ES_SX32(ES_W("%s")","ES_W("%s")",-")",%s,=", LA_RK(), LA_RJ(), LA_RD());
		break;
	case LA_INS_SUB_D:
		r_strbuf_appendf (&op->esil, "%s,%s,-,%s,=", LA_RK(), LA_RJ(), LA_RD());
		break;
	case LA_INS_MUL_W:
		r_strbuf_appendf (&op->esil, ES_SX32(ES_W(ES_W("%s")","ES_W("%s")",*"))",%s,=", LA_RJ(), LA_RK(), LA_RD());
		break;
	case LA_INS_MULH_WU:
	case LA_INS_MULH_W:
		r_strbuf_appendf (&op->esil, ES_WH(ES_W("%s")","ES_W("%s")",*")",%s,=", LA_RJ(), LA_RK(), LA_RD());
		break;
	case LA_INS_MUL_D:
		r_strbuf_appendf (&op->esil, "%s,%s,*,%s,=", LA_RJ(), LA_RK(), LA_RD());
		break;
		/* FIXME */
	case LA_INS_MULH_DU:
	case LA_INS_MULH_D:
		break;
	case LA_INS_DIV_WU:
	case LA_INS_DIV_W:
		r_strbuf_appendf (&op->esil, ES_W("%s")","ES_W("%s")",/,%s,=", LA_RJ(), LA_RK(), LA_RD());
		break;
	case LA_INS_MOD_WU:
	case LA_INS_MOD_W:
		r_strbuf_appendf (&op->esil, ES_SX32(ES_W("%s")","ES_W("%s")",%%")",%s,=", LA_RJ(), LA_RK(), LA_RD());
		break;
		/* FIXME rk only bits 0~4 are used*/
	case LA_INS_SLL_W:
		r_strbuf_appendf (&op->esil, ES_SX32("%s,"ES_W("%s")",<<")",%s,=", LA_RK(), LA_RJ(), LA_RD());
		break;
	case LA_INS_SRL_W:
		r_strbuf_appendf (&op->esil,ES_SX32("%s,"ES_W("%s")",>>")",%s,=", LA_RK(), LA_RJ(), LA_RD());
		break;
	case LA_INS_SRA_W:
		r_strbuf_appendf (&op->esil,ES_SX32("%s,"ES_W("%s")",>>>>")",%s,=", LA_RK(), LA_RJ(), LA_RD());
		break;
	case LA_INS_ROTR_W:
		r_strbuf_appendf (&op->esil,ES_SX32("%s,"ES_W("%s")",>>>")",%s,=", LA_RK(), LA_RJ(), LA_RD());
		break;
	case LA_INS_SLLI_W:
		r_strbuf_appendf (&op->esil,ES_SX32("%d,"ES_W("%s")",<<")",%s,=", I_I5(opcode), LA_RJ(), LA_RD());
		break;
	case LA_INS_SRLI_W:
		r_strbuf_appendf (&op->esil,ES_SX32("%d,"ES_W("%s")",>>")",%s,=", I_I5(opcode), LA_RJ(), LA_RD());
		break;
	case LA_INS_SRAI_W:
		r_strbuf_appendf (&op->esil,ES_SX32("%d,"ES_W("%s")",>>>>")",%s,=", I_I5(opcode), LA_RJ(), LA_RD());
		break;
	case LA_INS_ROTRI_W:
		r_strbuf_appendf (&op->esil,ES_SX32("%d,"ES_W("%s")",>>>")",%s,=", I_I5(opcode), LA_RJ(), LA_RD());
		break;
	case LA_INS_ALSL_WU:
	case LA_INS_ALSL_W:
		r_strbuf_appendf (&op->esil,ES_W(ES_W("%s")",%d,"ES_W("%s")",<<,+")",%s,=",LA_RK() ,I_SA2(opcode)+1, LA_RJ(), LA_RD());
		break;
	case LA_INS_ALSL_D:
		r_strbuf_appendf (&op->esil,"%s,%d,%s,<<,+,%s,=", LA_RK(), I_SA2(opcode), LA_RJ(), LA_RD());
		break;
		/* FIXME rk only bits 0~5 are used*/
	case LA_INS_SLL_D:
		r_strbuf_appendf (&op->esil,"%s,%s,<<,%s,=", LA_RK(), LA_RJ(), LA_RD());
		break;
	case LA_INS_SRL_D:
		r_strbuf_appendf (&op->esil,"%s,%s,>>,%s,=", LA_RK(), LA_RJ(), LA_RD());
		break;
	case LA_INS_SRA_D:
		r_strbuf_appendf (&op->esil,"%s,%s,>>>>,%s,=", LA_RK(), LA_RJ(), LA_RD());
		break;
	case LA_INS_ROTR_D:
		r_strbuf_appendf (&op->esil,"%s,%s,>>>,%s,=", LA_RK(), LA_RJ(), LA_RD());
		break;
	case LA_INS_SLLI_D:
		r_strbuf_appendf (&op->esil,"%d,%s,<<,%s,=", I_I6(opcode), LA_RJ(), LA_RD());
		break;
	case LA_INS_SRLI_D:
		r_strbuf_appendf (&op->esil,"%d,%s,>>,%s,=", I_I6(opcode), LA_RJ(), LA_RD());
		break;
	case LA_INS_SRAI_D:
		r_strbuf_appendf (&op->esil,"%d,%s,>>>>,%s,=", I_I6(opcode), LA_RJ(), LA_RD());
		break;
	case LA_INS_ROTRI_D:
		r_strbuf_appendf (&op->esil,"%d,%s,>>>,%s,=", I_I6(opcode), LA_RJ(), LA_RD());
		break;
	case LA_INS_MOVE:
		r_strbuf_appendf (&op->esil,"%s,%s,=",LA_RJ(), LA_RD());
		break;
	case LA_INS_AND:
		r_strbuf_appendf (&op->esil,"%s,%s,&,%s,=", LA_RJ(), LA_RK(),LA_RD());
		break;
	case LA_INS_OR:
		r_strbuf_appendf (&op->esil,"%s,%s,|,%s,=", LA_RJ(), LA_RK(),LA_RD());
		break;
	case LA_INS_XOR:
		r_strbuf_appendf (&op->esil,"%s,%s,^,%s,=", LA_RJ(), LA_RK(),LA_RD());
		break;
	case LA_INS_NOR:
		r_strbuf_appendf (&op->esil,"%s,%s,|,0xffffffff,^,%s,=", LA_RJ(), LA_RK(),LA_RD());
		break;
	case LA_INS_ANDN:
		r_strbuf_appendf (&op->esil,"%s,^,0xffffffff,%s,&,%s,=", LA_RK(), LA_RJ(),LA_RD());
		break;
	case LA_INS_ORN:
		r_strbuf_appendf (&op->esil,"%s,^,0xffffffff,%s,|,%s,=", LA_RK(), LA_RJ(),LA_RD());
		break;
	case LA_INS_ANDI:
		r_strbuf_appendf (&op->esil,"%d,%s,&,%s,=", I_I12(opcode), LA_RJ(),LA_RD());
		break;
	case LA_INS_ORI:
		r_strbuf_appendf (&op->esil,"%d,%s,|,%s,=", I_I12(opcode), LA_RJ(), LA_RD());
		break;
	case LA_INS_XORI:
		r_strbuf_appendf (&op->esil,"%d,%s,^,%s,=", I_I12(opcode), LA_RJ(), LA_RD());
		break;
	case LA_INS_B:
		r_strbuf_appendf (&op->esil,"0x%"LA_PFM",pc,+,pc,=", I26s2_SX(opcode));
		break;
	case LA_INS_BL:
		r_strbuf_appendf (&op->esil,"4,pc,+,ra,=,0x%"LA_PFM",pc,+,pc,=", I26s2_SX(opcode));
		break;
	case LA_INS_JIRL:
		r_strbuf_appendf (&op->esil,"4,pc,+,%s,=,0x%"LA_PFM",pc,+,pc,=",LA_RD(), I16s2_SX(opcode));
		break;
	case LA_INS_BEQ:
		r_strbuf_appendf (&op->esil,"%s,%s,==,$z,?{,0x%"LA_PFM",pc,+,pc,=,}",LA_RJ(), LA_RD(), I16s2_SX(opcode));
		break;
	case LA_INS_BEQZ:
		r_strbuf_appendf (&op->esil,"%s,0,==,$z,?{,0x%"LA_PFM",pc,+,pc,=,}",LA_RJ(),  I21s2_SX(opcode));
		break;
	case LA_INS_BNEZ:
		r_strbuf_appendf (&op->esil,"%s,0,==,$z,!,?{,0x%"LA_PFM",pc,+,pc,=,}",LA_RJ(),  I21s2_SX(opcode));
		break;
	case LA_INS_BNE:
		r_strbuf_appendf (&op->esil,"%s,%s,==,$z,!,?{,0x%"LA_PFM",pc,+,pc,=,}",LA_RJ(), LA_RD(), I16s2_SX(opcode));
		break;
		/* FIXME U means unsigned comparison*/
	case LA_INS_BLTU:
	case LA_INS_BLT:
		r_strbuf_appendf (&op->esil, "%s,%s,<,?{,0x%"LA_PFM",pc,+,pc,=,}",LA_RD(), LA_RJ(), I16s2_SX(opcode));
		break;
	case LA_INS_BGEU:
	case LA_INS_BGE:
		r_strbuf_appendf (&op->esil, "%s,%s,>,?{,0x%"LA_PFM",pc,+,pc,=,}",LA_RD(), LA_RJ(), I16s2_SX(opcode));
		break;
	default:
		break;
	}
	return 0;
}

static int insn_fprintf_func(void *stream, const char *format, ...) {
	int ret = 1;
	va_list ap;
	if (!stream || !format) {
		return 0;
	}
	va_start (ap, format);
	ret = r_strbuf_vappendf (stream, format, ap);
	va_end (ap);

	return ret;
}

static int insn_read_func(bfd_vma memaddr, bfd_byte *addr, unsigned int length, struct disassemble_info *info) {
	PluginData *pd = info->private_data;
	int delta = (memaddr - pd->insn_offset);
	if (delta < 0) {
		return -1;      // disable backward reads
	}
	if ((delta + length) > INSNLEN) {
		return -1;
	}
	memcpy (addr, pd->insn_bytes + delta, length);
	return 0;
}

static void insn_memory_error_func(int status, bfd_vma memaddr, struct disassemble_info *info) {
	//TODO
}

static bool decode(RArchSession *as, RAnalOp *op, RAnalOpMask mask) {
	const ut64 addr = op->addr;
	const ut8 *b = op->bytes;
	const int len = op->size;

	struct loongarch_ASE *ase = NULL;
	const struct loongarch_anal_opcode *it;
	ut32 opcode; // , optype;
	ut32 insn_id = 0;
	if (!op || (len < INSNLEN)) {
		return false;
	}
	op->type = R_ANAL_OP_TYPE_UNK;
	op->size = INSNLEN;
	op->addr = addr;
	// Be endian aware
	opcode = r_read_le32 (b);

	/* eprintf("opcode: 0x%x \n", opcode); */
	// optype = 0;

	PluginData *pd = as->data;
	for (ase = pd->la_ases; ase->opcode; ase++) {
		if (!ase->opc_htab_inited) {
			for (it=ase->opcode; it->match; it++) {
				if (!ase->la_opcode_ht[LA_INSN_HASH(it->match)]) {
					ase->la_opcode_ht[LA_INSN_HASH(it->match)] = it;
				}
			}
			int i;
			for (i = 0; i < HT_NUM; i++) {
				if (!ase->la_opcode_ht[i]) {
					ase->la_opcode_ht[i]=it;
				}
			}
			ase->opc_htab_inited = 1;
		}

		it = ase->la_opcode_ht[LA_INSN_HASH(opcode)];
		/* it = ase->opcode; */
		for (; it->match; it++) {
			// optype ++;
			if ((opcode & it->mask) == it->match) {
				insn_id = it->index;
				op->type = it->r_type;
				/* break; //FIXME */
			}
		}
	}
	op->id = insn_id;

	switch (insn_id) {
	case LA_INS_BEQ:
	case LA_INS_BNE:
	case LA_INS_BLT:
	case LA_INS_BGE:
	case LA_INS_BLTU:
	case LA_INS_BGEU:
		op->jump = addr + I16s2_SX(opcode);
		op->fail = addr + INSNLEN;
		break;
	case LA_INS_BEQZ:
	case LA_INS_BNEZ:
		op->jump = addr + I21s2_SX(opcode);
		op->fail = addr + INSNLEN;
		break;
	case LA_INS_B:
		op->jump = addr + I26s2_SX(opcode);
		break;
	case LA_INS_BL:
		op->jump = addr + I26s2_SX(opcode);
		op->fail = addr + INSNLEN;
		break;
	case LA_INS_JIRL:
		//TODO
		/* op->jump = addr + I_I16s2(opcode) + rj; */
		break;
	case LA_INS_LD_B:
		break;
	case LA_INS_PCADDU12I:
		//TODO
		/* op->val = sign_extend32(I_I20(opcode)<<12, 32); */
		break;
	default:
		break;
	}

	if (mask & R_ARCH_OP_MASK_ESIL) {
		if (analop_esil (as, op, opcode)) {
			r_strbuf_fini (&op->esil);
		}
	}
	if (mask & R_ARCH_OP_MASK_VAL) {
		//TODO: add op_fillval (anal, op, &insn);
	}

	if (mask & R_ARCH_OP_MASK_DISASM) {
		PluginData *pd = as->data;
		struct disassemble_info disasm_obj;
		int n = 0;
		RStrBuf *insn_strbuf = r_strbuf_new ("");

		pd->insn_offset = addr;
		/*Looks kind of lame*/
		memcpy (pd->insn_bytes, b, INSNLEN);

		disasm_obj.private_data = pd;
		disasm_obj.fprintf_func = &insn_fprintf_func;
		disasm_obj.memory_error_func = &insn_memory_error_func;
		disasm_obj.read_memory_func = &insn_read_func;
		disasm_obj.stream = insn_strbuf;
		n = print_insn_loongarch (addr, &disasm_obj);
		if (n < 0) {
			op->mnemonic = strdup ("invalid");
		} else {
			op->mnemonic = strdup (insn_strbuf->buf);
		}
		r_strbuf_free (insn_strbuf);
	}
	return true;
}

static int archinfo(RArchSession *as, ut32 q) {
	return INSNLEN;
}

/* Set the profile register */
static char *regs(RArchSession* as) {
	const char *p =
	"=PC	pc\n"
	"=SP	sp\n"
	"=BP	fp\n"
	"=SN	v0\n"
	"=A0	a0\n"
	"=A1	a1\n"
	"=A2	a2\n"
	"=A3	a3\n"
	"=A4	a4\n"
	"=A5	a5\n"
	"=A6	a6\n"
	"=A7	a7\n"
	"=R0    v0\n"
	"=R1    v1\n"
	"gpr	zero	.64	?	0\n"
	"gpr	ra	.64	8	0\n"
	"gpr	tp	.64	16	0\n"
	"gpr	sp	.64	24	0\n"
	/* args */
	"gpr	a0	.64	32	0\n"
	"gpr	a1	.64	40	0\n"
	/*FIXME v0 v1 and a0 a1 are overlapping*/
	"gpr	a2	.64	48	0\n"
	"gpr	a3	.64	56	0\n"
	"gpr	a4	.64	64	0\n"
	"gpr	a5	.64	72	0\n"
	"gpr	a6	.64	80	0\n"
	"gpr	a7	.64	88	0\n"
	/* tmp */
	"gpr	t0	.64	96	0\n"
	"gpr	t1	.64	104	0\n"
	"gpr	t2	.64	112	0\n"
	"gpr	t3	.64	120	0\n"
	"gpr	t4	.64	128	0\n"
	"gpr	t5	.64	136	0\n"
	"gpr	t6	.64	144	0\n"
	"gpr	t7	.64	152	0\n"
	"gpr	t8	.64	160	0\n"
	"gpr	x	.64	168	0\n"
	"gpr	fp	.64	176	0\n"
	/* saved */
	"gpr	s0	.64	184	0\n"
	"gpr	s1	.64	192	0\n"
	"gpr	s2	.64	200	0\n"
	"gpr	s3	.64	208	0\n"
	"gpr	s4	.64	216	0\n"
	"gpr	s5	.64	224	0\n"
	"gpr	s6	.64	232	0\n"
	"gpr	s7	.64	240	0\n"
	"gpr	s8	.64	248	0\n"
	/* extra */
	"gpr	pc	.64	272	0\n"
	;
	return strdup (p);
}

static bool init(RArchSession *s) {
	R_RETURN_VAL_IF_FAIL (s, false);
	if (s->data) {
		R_LOG_WARN ("Already initialized");
		return false;
	}

	s->data = R_NEW0 (PluginData);
	PluginData *pd = s->data;
	if (!pd) {
		return false;
	}

	memcpy (pd->la_ases, la_ases_initial, sizeof (la_ases_initial));
	return true;
}

static bool fini(RArchSession *s) {
	R_RETURN_VAL_IF_FAIL (s, false);
	R_FREE (s->data);
	return true;
}

const RArchPlugin r_arch_plugin_loongarch_gnu = {
	.meta = {
		.name = "loongarch",
		.desc = "loongson loongarch code analysis plugin",
		.license = "LGPL3",
	},
	.arch = "loongarch",
	.bits = 64,
	.info = archinfo,
	.decode = decode,
	.regs = regs,
	.init = init,
	.fini = fini,
};

#ifndef R2_PLUGIN_INCORE
R_API RLibStruct radare_plugin = {
	.type = R_LIB_TYPE_ARCH,
	.data = &r_arch_plugin_loongarch_gnu
};
#endif
