public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 7/7][LoongArch] Opcodes support
@ 2021-08-14  8:20 Paul Hua
  2021-08-14 17:58 ` Fangrui Song
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Paul Hua @ 2021-08-14  8:20 UTC (permalink / raw)
  To: binutils; +Cc: Xu Chenghua, huangpei, liuzhensong, chenglulu, caiyinyu

[-- Attachment #1: Type: text/plain, Size: 1 bytes --]



[-- Attachment #2: 0007-opcodes-LoongArch-Opcodes-Port.patch --]
[-- Type: text/x-patch, Size: 81423 bytes --]

From cfe919101a3409317ec92ebd6f5c388b5ea9dce4 Mon Sep 17 00:00:00 2001
From: liuzhensong <liuzhensong@loongson.cn>
Date: Sat, 14 Aug 2021 11:37:28 +0800
Subject: [PATCH 7/7] opcodes: LoongArch Opcodes Port.

  include/dis-asm.h
  include/opcode/loongarch.h
  opcodes/Makefile.am
  opcodes/Makefile.in
  opcodes/configure
  opcodes/configure.ac
  opcodes/disassemble.c
  opcodes/disassemble.h
  opcodes/loongarch-coder.c
  opcodes/loongarch-dis.c
  opcodes/loongarch-opc.c
---
 include/dis-asm.h          |   1 +
 include/opcode/loongarch.h | 220 +++++++++++
 opcodes/Makefile.am        |   3 +
 opcodes/Makefile.in        |   6 +
 opcodes/configure          |   1 +
 opcodes/configure.ac       |   1 +
 opcodes/disassemble.c      |   9 +
 opcodes/disassemble.h      |   1 +
 opcodes/loongarch-coder.c  | 558 ++++++++++++++++++++++++++
 opcodes/loongarch-dis.c    | 340 ++++++++++++++++
 opcodes/loongarch-opc.c    | 782 +++++++++++++++++++++++++++++++++++++
 11 files changed, 1922 insertions(+)
 create mode 100644 include/opcode/loongarch.h
 create mode 100644 opcodes/loongarch-coder.c
 create mode 100644 opcodes/loongarch-dis.c
 create mode 100644 opcodes/loongarch-opc.c

diff --git a/include/dis-asm.h b/include/dis-asm.h
index 0b91ab47ff3..c0bc1d542cf 100644
--- a/include/dis-asm.h
+++ b/include/dis-asm.h
@@ -307,6 +307,7 @@ extern void print_arm_disassembler_options (FILE *);
 extern void print_arc_disassembler_options (FILE *);
 extern void print_s390_disassembler_options (FILE *);
 extern void print_wasm32_disassembler_options (FILE *);
+extern void print_loongarch_disassembler_options (FILE *);
 extern bool aarch64_symbol_is_valid (asymbol *, struct disassemble_info *);
 extern bool arm_symbol_is_valid (asymbol *, struct disassemble_info *);
 extern bool csky_symbol_is_valid (asymbol *, struct disassemble_info *);
diff --git a/include/opcode/loongarch.h b/include/opcode/loongarch.h
new file mode 100644
index 00000000000..c052b46878b
--- /dev/null
+++ b/include/opcode/loongarch.h
@@ -0,0 +1,220 @@
+/* LoongArch assembler/disassembler support.
+
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GNU Binutils.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the license, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; see the file COPYING3.  If not,
+   see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _LOONGARCH_H_
+#define _LOONGARCH_H_
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+  typedef uint32_t insn_t;
+
+  struct loongarch_opcode
+  {
+    const insn_t match;
+    const insn_t mask; /* High 1 byte is main opcode and it must be 0xf.  */
+#define LARCH_INSN_OPC(insn) ((insn & 0xf0000000) >> 28)
+    const char *const name;
+
+    /* ACTUAL PARAMETER:
+
+  // BNF with regular expression.
+args : token* end
+
+  // just few char separate 'iden'
+token : ','
+| '('
+| ')'
+| iden	     // maybe a label (include at least one alphabet),
+		      maybe a number, maybe a expr
+| regname
+
+regname : '$' iden
+
+iden : [a-zA-Z0-9\.\+\-]+
+
+end : '\0'
+
+
+FORMAT: A string to describe the format of actual parameter including
+bit field infomation.  For example, "r5:5,r0:5,sr10:16<<2" matches
+"$12,$13,12345" and "$4,$7,a_label".  That 'sr' means the instruction
+may need relocate. '10:16' means bit field of instruction.
+In a 'format', every 'escape's can be replaced to 'iden' or 'regname'
+acrroding to its meaning.  We fill all information needed by
+disassembing and assembing to 'format'.
+
+  // BNF with regular expression.
+format : escape (literal+ escape)* literal* end
+| (literal+ escape)* literal* end
+
+end : '\0'       // Get here means parse end.
+
+  // The intersection between any two among FIRST (end), FIRST
+     (literal) and FIRST (escape) must be empty.
+  // So we can build a simple parser.
+literal : ','
+| '('
+| ')'
+
+  // Double '<'s means the real number is the immediate after shifting left.
+escape : esc_ch bit_field '<' '<' dec2
+| esc_ch bit_field
+| esc_ch    // for MACRO. non-macro format must indicate 'bit_field'
+
+  // '|' means to concatenate nonadjacent bit fields
+  // For example, "10:16|0:4" means
+  // "16 bits starting from the 10th bit concatenating with 4 bits
+     starting from the 0th bit".
+  // This is to say "[25..10]||[3..0]" (little endian).
+b_field : dec2 ':' dec2
+| dec2 ':' dec2 '|' bit_field
+
+esc_ch : 's' 'r'   // signed immediate or label need relocate
+| 's'       // signed immediate no need relocate
+| 'u'       // unsigned immediate
+| 'l'       // label needed relocate
+| 'r'       // general purpose registers
+| 'f'       // FPU registers
+| 'v'       // 128 bit SIMD register
+| 'x'       // 256 bit SIMD register
+
+dec2 : [1-9][0-9]?
+| 0
+
+*/
+    const char *const format;
+
+    /* MACRO: Indicate how a macro instruction expand for assembling.
+The main is to replace the '%num'(means the 'num'th 'escape' in 'format')
+in 'macro' string to get the real instruction.
+
+Maybe need
+*/
+    const char *const macro;
+    const int *include;
+    const int *exclude;
+
+    const unsigned long pinfo;
+#define USELESS 0x0l
+  };
+
+  struct hash_control;
+
+  struct loongarch_ase
+  {
+    const int *enabled;
+    struct loongarch_opcode *const opcodes;
+    const int *include;
+    const int *exclude;
+
+    /* For disassemble to create main opcode hash table.  */
+    const struct loongarch_opcode *opc_htab[16];
+    unsigned char opc_htab_inited;
+
+    /* For GAS to create hash table.  */
+    struct htab *name_hash_entry;
+  };
+
+  extern int is_unsigned (const char *);
+  extern int is_signed (const char *);
+  extern int is_label_with_addend (const char *);
+  extern int is_label (const char *);
+  extern int is_branch_label (const char *);
+
+  extern int loongarch_get_bit_field_width (const char *bit_field, char **end);
+  extern int32_t loongarch_decode_imm (const char *bit_field, insn_t insn,
+				       int si);
+
+#define MAX_ARG_NUM_PLUS_2 9
+
+  extern size_t loongarch_split_args_by_comma (char *args,
+					       const char *arg_strs[]);
+  extern char *loongarch_cat_splited_strs (const char *arg_strs[]);
+  extern insn_t loongarch_foreach_args (
+    const char *format, const char *arg_strs[],
+    int32_t (*helper) (char esc1, char esc2, const char *bit_field,
+		       const char *arg, void *context),
+    void *context);
+
+  extern int loongarch_check_format (const char *format);
+  extern int loongarch_check_macro (const char *format, const char *macro);
+
+  extern char *loongarch_expand_macro_with_format_map (
+    const char *format, const char *macro, const char *const arg_strs[],
+    const char *(*map) (char esc1, char esc2, const char *arg),
+    char *(*helper) (const char *const arg_strs[], void *context),
+    void *context);
+  extern char *loongarch_expand_macro (
+    const char *macro, const char *const arg_strs[],
+    char *(*helper) (const char *const arg_strs[], void *context),
+    void *context);
+  extern size_t loongarch_bits_imm_needed (int64_t imm, int si);
+
+  extern void loongarch_eliminate_adjacent_repeat_char (char *dest, char c);
+
+  extern int loongarch_parse_dis_options (const char *opts_in);
+  extern void loongarch_disassemble_one (
+    int64_t pc, insn_t insn,
+    int (*fprintf_func) (void *stream, const char *format, ...), void *stream);
+
+  extern const char *const loongarch_r_normal_name[32];
+  extern const char *const loongarch_r_lp64_name[32];
+  extern const char *const loongarch_r_lp64_name1[32];
+  extern const char *const loongarch_f_normal_name[32];
+  extern const char *const loongarch_f_lp64_name[32];
+  extern const char *const loongarch_f_lp64_name1[32];
+  extern const char *const loongarch_c_normal_name[8];
+  extern const char *const loongarch_cr_normal_name[4];
+  extern const char *const loongarch_v_normal_name[32];
+  extern const char *const loongarch_x_normal_name[32];
+
+  extern struct loongarch_ase loongarch_ASEs[];
+
+  extern struct loongarch_ASEs_option
+  {
+    int ase_fix;
+    int ase_float;
+    int ase_128vec;
+    int ase_256vec;
+
+    int addrwidth_is_32;
+    int addrwidth_is_64;
+    int rlen_is_32;
+    int rlen_is_64;
+    int la_local_with_abs;
+    int la_global_with_pcrel;
+    int la_global_with_abs;
+
+    int abi_is_lp32;
+    int abi_is_lp64;
+  } LARCH_opts;
+
+  extern size_t loongarch_insn_length (insn_t insn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LOONGARCH_H_ */
diff --git a/opcodes/Makefile.am b/opcodes/Makefile.am
index 0e04b4c05c4..c45fc295665 100644
--- a/opcodes/Makefile.am
+++ b/opcodes/Makefile.am
@@ -164,6 +164,9 @@ TARGET_LIBOPCODES_CFILES = \
 	lm32-ibld.c \
 	lm32-opc.c \
 	lm32-opinst.c \
+	loongarch-opc.c \
+	loongarch-dis.c \
+	loongarch-coder.c \
 	m10200-dis.c \
 	m10200-opc.c \
 	m10300-dis.c \
diff --git a/opcodes/Makefile.in b/opcodes/Makefile.in
index 42c15f00d30..8ba01c9f8f9 100644
--- a/opcodes/Makefile.in
+++ b/opcodes/Makefile.in
@@ -555,6 +555,9 @@ TARGET_LIBOPCODES_CFILES = \
 	lm32-ibld.c \
 	lm32-opc.c \
 	lm32-opinst.c \
+	loongarch-opc.c \
+	loongarch-dis.c \
+	loongarch-coder.c \
 	m10200-dis.c \
 	m10200-opc.c \
 	m10300-dis.c \
@@ -973,6 +976,9 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lm32-ibld.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lm32-opc.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lm32-opinst.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loongarch-coder.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loongarch-dis.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loongarch-opc.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m10200-dis.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m10200-opc.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m10300-dis.Plo@am__quote@
diff --git a/opcodes/configure b/opcodes/configure
index 3513e408ce1..aa46453d815 100755
--- a/opcodes/configure
+++ b/opcodes/configure
@@ -12294,6 +12294,7 @@ if test x${all_targets} = xfalse ; then
 	bfd_z80_arch)		ta="$ta z80-dis.lo" ;;
 	bfd_z8k_arch)		ta="$ta z8k-dis.lo" ;;
 	bfd_bpf_arch)		ta="$ta bpf-asm.lo bpf-desc.lo bpf-dis.lo bpf-ibld.lo bpf-opc.lo" using_cgen=yes ;;
+	bfd_loongarch_arch)		ta="$ta loongarch-dis.lo loongarch-opc.lo loongarch-coder.lo" ;;
 
 	"")			;;
 	*)		as_fn_error $? "*** unknown target architecture $arch" "$LINENO" 5 ;;
diff --git a/opcodes/configure.ac b/opcodes/configure.ac
index e564f067334..4853b9e32d7 100644
--- a/opcodes/configure.ac
+++ b/opcodes/configure.ac
@@ -355,6 +355,7 @@ if test x${all_targets} = xfalse ; then
 	bfd_z80_arch)		ta="$ta z80-dis.lo" ;;
 	bfd_z8k_arch)		ta="$ta z8k-dis.lo" ;;
 	bfd_bpf_arch)		ta="$ta bpf-asm.lo bpf-desc.lo bpf-dis.lo bpf-ibld.lo bpf-opc.lo" using_cgen=yes ;;
+	bfd_loongarch_arch)	ta="$ta loongarch-dis.lo loongarch-opc.lo loongarch-coder.lo" ;;
 
 	"")			;;
 	*)		AC_MSG_ERROR(*** unknown target architecture $arch) ;;
diff --git a/opcodes/disassemble.c b/opcodes/disassemble.c
index 8590e945c58..61e666c1822 100644
--- a/opcodes/disassemble.c
+++ b/opcodes/disassemble.c
@@ -49,6 +49,7 @@
 #define ARCH_ip2k
 #define ARCH_iq2000
 #define ARCH_lm32
+#define ARCH_loongarch
 #define ARCH_m32c
 #define ARCH_m32r
 #define ARCH_m68hc11
@@ -551,6 +552,11 @@ disassembler (enum bfd_architecture a,
     case bfd_arch_tilepro:
       disassemble = print_insn_tilepro;
       break;
+#endif
+#ifdef ARCH_loongarch
+    case bfd_arch_loongarch:
+      disassemble = print_insn_loongarch;
+      break;
 #endif
     default:
       return 0;
@@ -591,6 +597,9 @@ disassembler_usage (FILE *stream ATTRIBUTE_UNUSED)
 #ifdef ARCH_wasm32
   print_wasm32_disassembler_options (stream);
 #endif
+#ifdef ARCH_loongarch
+  print_loongarch_disassembler_options (stream);
+#endif
 
   return;
 }
diff --git a/opcodes/disassemble.h b/opcodes/disassemble.h
index 8ee54dc9494..4e3ea2328e0 100644
--- a/opcodes/disassemble.h
+++ b/opcodes/disassemble.h
@@ -100,6 +100,7 @@ extern int print_insn_xtensa		(bfd_vma, disassemble_info *);
 extern int print_insn_z80		(bfd_vma, disassemble_info *);
 extern int print_insn_z8001		(bfd_vma, disassemble_info *);
 extern int print_insn_z8002		(bfd_vma, disassemble_info *);
+extern int print_insn_loongarch		(bfd_vma, disassemble_info *);
 
 extern disassembler_ftype csky_get_disassembler (bfd *);
 extern disassembler_ftype rl78_get_disassembler (bfd *);
diff --git a/opcodes/loongarch-coder.c b/opcodes/loongarch-coder.c
new file mode 100644
index 00000000000..ca00c326bae
--- /dev/null
+++ b/opcodes/loongarch-coder.c
@@ -0,0 +1,558 @@
+/* LoongArch opcode support.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of the GNU opcodes library.
+
+   This library is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   It is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; see the file COPYING3.  If not,
+   see <http://www.gnu.org/licenses/>.  */
+#include "sysdep.h"
+#include "opcode/loongarch.h"
+
+int
+is_unsigned (const char *c_str)
+{
+  if (c_str[0] == '0' && (c_str[1] == 'x' || c_str[1] == 'X'))
+    {
+      c_str += 2;
+      while (('a' <= *c_str && *c_str <= 'f')
+	     || ('A' <= *c_str && *c_str <= 'F')
+	     || ('0' <= *c_str && *c_str <= '9'))
+	c_str++;
+    }
+  else if (*c_str == '\0')
+    return 0;
+  else
+    while ('0' <= *c_str && *c_str <= '9')
+      c_str++;
+  return *c_str == '\0';
+}
+
+int
+is_signed (const char *c_str)
+{
+  return *c_str == '-' ? is_unsigned (c_str + 1) : is_unsigned (c_str);
+}
+
+static int
+is_internal_label (const char *c_str)
+{
+  do
+    {
+      if (*c_str != ':')
+	break;
+      c_str++;
+      if (!('0' <= *c_str && *c_str <= '9'))
+	break;
+      while ('0' <= *c_str && *c_str <= '9')
+	c_str++;
+      if (*c_str != 'b' && *c_str != 'f')
+	break;
+      c_str++;
+      return *c_str == '\0';
+    }
+  while (0);
+  return 0;
+}
+
+int
+is_label (const char *c_str)
+{
+  if (is_internal_label (c_str))
+    return 1;
+  else if ('0' <= *c_str && *c_str <= '9')
+    {
+      /* [0-9]+[bf]  */
+      while ('0' <= *c_str && *c_str <= '9')
+	c_str++;
+      return *c_str == 'b' || *c_str == 'f';
+    }
+  else if (('a' <= *c_str && *c_str <= 'z')
+	   || ('A' <= *c_str && *c_str <= 'Z')
+	   || *c_str == '.'
+	   || *c_str == '_'
+	   || *c_str == '$')
+    {
+      /* [a-zA-Z\._\$][0-9a-zA-Z\._\$]*  */
+      while (('a' <= *c_str && *c_str <= 'z')
+	     || ('A' <= *c_str && *c_str <= 'Z')
+	     || ('0' <= *c_str && *c_str <= '9')
+	     || *c_str == '.'
+	     || *c_str == '_'
+	     || *c_str == '$')
+	c_str++;
+      return *c_str == '\0';
+    }
+  else
+    return 0;
+}
+
+int
+is_label_with_addend (const char *c_str)
+{
+  if (is_internal_label (c_str))
+    return 1;
+  else if ('0' <= *c_str && *c_str <= '9')
+    {
+      /* [0-9]+[bf]  */
+      while ('0' <= *c_str && *c_str <= '9')
+	c_str++;
+      if (*c_str == 'b' || *c_str == 'f')
+	c_str++;
+      else
+	return 0;
+      return *c_str == '\0'
+		       || ((*c_str == '-' || *c_str == '+')
+			   && is_unsigned (c_str + 1));
+    }
+  else if (('a' <= *c_str && *c_str <= 'z')
+	   || ('A' <= *c_str && *c_str <= 'Z')
+	   || *c_str == '.'
+	   || *c_str == '_'
+	   || *c_str == '$')
+    {
+      /* [a-zA-Z\._\$][0-9a-zA-Z\._\$]*  */
+      while (('a' <= *c_str && *c_str <= 'z')
+	     || ('A' <= *c_str && *c_str <= 'Z')
+	     || ('0' <= *c_str && *c_str <= '9')
+	     || *c_str == '.'
+	     || *c_str == '_'
+	     || *c_str == '$')
+	c_str++;
+      return *c_str == '\0'
+		       || ((*c_str == '-' || *c_str == '+')
+			   && is_unsigned (c_str + 1));
+    }
+  else
+    return 0;
+}
+
+int
+loongarch_get_bit_field_width (const char *bit_field, char **end)
+{
+  int width = 0;
+  char has_specify = 0, *bit_field_1 = (char *) bit_field;
+  if (bit_field_1 && *bit_field_1 != '\0')
+    while (1)
+      {
+	strtol (bit_field_1, &bit_field_1, 10);
+
+	if (*bit_field_1 != ':')
+	  break;
+	bit_field_1++;
+
+	width += strtol (bit_field_1, &bit_field_1, 10);
+	has_specify = 1;
+
+	if (*bit_field_1 != '|')
+	  break;
+	bit_field_1++;
+      }
+  if (end)
+    *end = bit_field_1;
+  return has_specify ? width : -1;
+}
+
+int32_t
+loongarch_decode_imm (const char *bit_field, insn_t insn, int si)
+{
+  int32_t ret = 0;
+  uint32_t t;
+  int len = 0, width, b_start;
+  char *bit_field_1 = (char *) bit_field;
+  while (1)
+    {
+      b_start = strtol (bit_field_1, &bit_field_1, 10);
+      if (*bit_field_1 != ':')
+	break;
+      width = strtol (bit_field_1 + 1, &bit_field_1, 10);
+      len += width;
+
+      t = insn;
+      t <<= sizeof (t) * 8 - width - b_start;
+      t >>= sizeof (t) * 8 - width;
+      ret <<= width;
+      ret |= t;
+
+      if (*bit_field_1 != '|')
+	break;
+      bit_field_1++;
+    }
+
+  if (*bit_field_1 == '<' && *(++bit_field_1) == '<')
+    {
+      width = atoi (bit_field_1 + 1);
+      ret <<= width;
+      len += width;
+    }
+  else if (*bit_field_1 == '+')
+    ret += atoi (bit_field_1 + 1);
+
+  if (si)
+    {
+      ret <<= sizeof (ret) * 8 - len;
+      ret >>= sizeof (ret) * 8 - len;
+    }
+  return ret;
+}
+
+static insn_t
+loongarch_encode_imm (const char *bit_field, int32_t imm)
+{
+  char *bit_field_1 = (char *) bit_field;
+  char *t = bit_field_1;
+  int width, b_start;
+  insn_t ret = 0, i;
+
+  width = loongarch_get_bit_field_width (t, &t);
+  if (width == -1)
+    return ret;
+
+  if (*t == '<' && *(++t) == '<')
+    width += atoi (t + 1);
+  else if (*t == '+')
+    imm -= atoi (t + 1);
+
+  imm <<= sizeof (imm) * 8 - width;
+  while (1)
+    {
+      b_start = strtol (bit_field_1, &bit_field_1, 10);
+      if (*bit_field_1 != ':')
+	break;
+      width = strtol (bit_field_1 + 1, &bit_field_1, 10);
+      i = imm;
+      i >>= sizeof (i) * 8 - width;
+      i <<= b_start;
+      ret |= i;
+      imm <<= width;
+
+      if (*bit_field_1 != '|')
+	break;
+      bit_field_1++;
+    }
+  return ret;
+}
+
+/* Parse such FORMAT
+     ""
+     "u"
+     "v0:5,r5:5,s10:10<<2"
+     "r0:5,r5:5,r10:5,u15:2+1"
+     "r,r,u0:5+32,u0:5+1"
+*/
+static int
+loongarch_parse_format (const char *format, char *esc1s, char *esc2s,
+			const char **bit_fields)
+{
+  size_t arg_num = 0;
+
+  if (*format == '\0')
+    goto end;
+
+  while (1)
+    {
+      /* esc1    esc2
+	 for "[a-zA-Z][a-zA-Z]?"  */
+      if (('a' <= *format && *format <= 'z')
+	  || ('A' <= *format && *format <= 'Z'))
+	{
+	  *esc1s++ = *format++;
+	  if (('a' <= *format && *format <= 'z')
+	      || ('A' <= *format && *format <= 'Z'))
+	    *esc2s++ = *format++;
+	  else
+	    *esc2s++ = '\0';
+	}
+      else
+	return -1;
+
+      arg_num++;
+      if (MAX_ARG_NUM_PLUS_2 - 2 < arg_num)
+	/* Need larger MAX_ARG_NUM_PLUS_2.  */
+	return -1;
+
+      *bit_fields++ = format;
+
+      if ('0' <= *format && *format <= '9')
+	{
+	  /* For "[0-9]+:[0-9]+(\|[0-9]+:[0-9]+)*".  */
+	  while (1)
+	    {
+	      while ('0' <= *format && *format <= '9')
+		format++;
+
+	      if (*format != ':')
+		return -1;
+	      format++;
+
+	      if (!('0' <= *format && *format <= '9'))
+		return -1;
+	      while ('0' <= *format && *format <= '9')
+		format++;
+
+	      if (*format != '|')
+		break;
+	      format++;
+	    }
+
+	  /* For "((\+|<<)[1-9][0-9]*)?".  */
+	  do
+	    {
+	      if (*format == '+')
+		format++;
+	      else if (format[0] == '<' && format[1] == '<')
+		format += 2;
+	      else
+		break;
+
+	      if (!('1' <= *format && *format <= '9'))
+		return -1;
+	      while ('0' <= *format && *format <= '9')
+		format++;
+	    }
+	  while (0);
+	}
+
+      if (*format == ',')
+	format++;
+      else if (*format == '\0')
+	break;
+      else
+	return -1;
+    }
+
+end:
+  *esc1s = '\0';
+  return 0;
+}
+
+size_t
+loongarch_split_args_by_comma (char *args, const char *arg_strs[])
+{
+  size_t num = 0;
+
+  if (*args)
+    arg_strs[num++] = args;
+  for (; *args; args++)
+    if (*args == ',')
+      {
+	if (MAX_ARG_NUM_PLUS_2 - 1 == num)
+	  break;
+	else
+	  *args = '\0', arg_strs[num++] = args + 1;
+      }
+  arg_strs[num] = NULL;
+  return num;
+}
+
+char *
+loongarch_cat_splited_strs (const char *arg_strs[])
+{
+  char *ret;
+  size_t n, l;
+
+  for (l = 0, n = 0; arg_strs[n]; n++)
+    l += strlen (arg_strs[n]);
+  ret = malloc (l + n + 1);
+  ret[0] = '\0';
+  if (0 < n)
+    strcat (ret, arg_strs[0]);
+  for (l = 1; l < n; l++)
+    strcat (ret, ","), strcat (ret, arg_strs[l]);
+  return ret;
+}
+
+insn_t
+loongarch_foreach_args (const char *format, const char *arg_strs[],
+			int32_t (*helper) (char esc1, char esc2,
+					   const char *bit_field,
+					   const char *arg, void *context),
+			void *context)
+{
+  char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1];
+  const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1];
+  size_t i;
+  insn_t ret = 0;
+  int ok;
+
+  ok = loongarch_parse_format (format, esc1s, esc2s, bit_fields) == 0;
+
+  /* Make sure the num of actual args is equal to the num of escape.  */
+  for (i = 0; esc1s[i] && arg_strs[i]; i++)
+    ;
+  ok = ok && !esc1s[i] && !arg_strs[i];
+
+  if (ok && helper)
+    {
+      for (i = 0; arg_strs[i]; i++)
+	ret |= loongarch_encode_imm (
+	  bit_fields[i],
+	  helper (esc1s[i], esc2s[i], bit_fields[i], arg_strs[i], context));
+      ret |= helper ('\0', '\0', NULL, NULL, context);
+    }
+
+  return ret;
+}
+
+int
+loongarch_check_format (const char *format)
+{
+  char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1];
+  const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1];
+
+  if (!format)
+    return -1;
+
+  return loongarch_parse_format (format, esc1s, esc2s, bit_fields);
+}
+
+int
+loongarch_check_macro (const char *format, const char *macro)
+{
+  int num_of_args;
+  char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1];
+  const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1];
+
+  if (!format || !macro
+      || loongarch_parse_format (format, esc1s, esc2s, bit_fields) != 0)
+    return -1;
+
+  for (num_of_args = 0; esc1s[num_of_args]; num_of_args++)
+    ;
+
+  for (; macro[0]; macro++)
+    if (macro[0] == '%')
+      {
+	macro++;
+	if ('1' <= macro[0] && macro[0] <= '9')
+	  {
+	    if (num_of_args < macro[0] - '0')
+	      /* Out of args num.  */
+	      return -1;
+	  }
+	else if (macro[0] == 'f')
+	  ;
+	else if (macro[0] == '%')
+	  ;
+	else
+	  return -1;
+      }
+  return 0;
+}
+
+static const char *
+I (char esc_ch1 ATTRIBUTE_UNUSED, char esc_ch2 ATTRIBUTE_UNUSED,
+   const char *c_str)
+{
+  return c_str;
+}
+
+char *
+loongarch_expand_macro_with_format_map (
+  const char *format, const char *macro, const char *const arg_strs[],
+  const char *(*map) (char esc1, char esc2, const char *arg),
+  char *(*helper) (const char *const arg_strs[], void *context), void *context)
+{
+  char esc1s[MAX_ARG_NUM_PLUS_2 - 1], esc2s[MAX_ARG_NUM_PLUS_2 - 1];
+  const char *bit_fields[MAX_ARG_NUM_PLUS_2 - 1];
+  const char *src;
+  char *dest;
+  char buffer[8192];
+
+  if (format)
+    loongarch_parse_format (format, esc1s, esc2s, bit_fields);
+
+  src = macro;
+  dest = buffer;
+
+  while (*src)
+    if (*src == '%')
+      {
+	src++;
+	if ('1' <= *src && *src <= '9')
+	  {
+	    size_t i = *src - '1';
+	    const char *t = map (esc1s[i], esc2s[i], arg_strs[i]);
+	    while (*t)
+	      *dest++ = *t++;
+	  }
+	else if (*src == '%')
+	  *dest++ = '%';
+	else if (*src == 'f' && helper)
+	  {
+	    char *b, *t;
+	    t = b = (*helper) (arg_strs, context);
+	    if (b)
+	      {
+		while (*t)
+		  *dest++ = *t++;
+		free (b);
+	      }
+	  }
+	src++;
+      }
+    else
+      *dest++ = *src++;
+
+  *dest = '\0';
+  return strdup (buffer);
+}
+
+char *
+loongarch_expand_macro (const char *macro, const char *const arg_strs[],
+			char *(*helper) (const char *const arg_strs[],
+					 void *context),
+			void *context)
+{
+  return loongarch_expand_macro_with_format_map (NULL, macro, arg_strs, I,
+						 helper, context);
+}
+
+size_t
+loongarch_bits_imm_needed (int64_t imm, int si)
+{
+  size_t ret;
+  if (si)
+    {
+      if (imm < 0)
+	{
+	  for (ret = 0; imm < 0; imm <<= 1, ret++)
+	    ;
+	  ret = 64 - ret + 1;
+	}
+      else
+	ret = loongarch_bits_imm_needed (imm, 0) + 1;
+    }
+  else
+    {
+      uint64_t t = imm;
+      for (ret = 0; t; t >>= 1, ret++)
+	;
+    }
+  return ret;
+}
+
+void
+loongarch_eliminate_adjacent_repeat_char (char *dest, char c)
+{
+  if (c == '\0')
+    return;
+  char *src = dest;
+  while (*dest)
+    {
+      while (src[0] == c && src[0] == src[1])
+	src++;
+      *dest++ = *src++;
+    }
+}
diff --git a/opcodes/loongarch-dis.c b/opcodes/loongarch-dis.c
new file mode 100644
index 00000000000..9b15a488c4f
--- /dev/null
+++ b/opcodes/loongarch-dis.c
@@ -0,0 +1,340 @@
+/* LoongArch opcode support.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of the GNU opcodes library.
+
+   This library is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   It is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; see the file COPYING3.  If not,
+   see <http://www.gnu.org/licenses/>.  */
+
+#include "sysdep.h"
+#include "disassemble.h"
+#include "opintl.h"
+#include "opcode/loongarch.h"
+#include "libiberty.h"
+#include <stdlib.h>
+
+static const struct loongarch_opcode *
+get_loongarch_opcode_by_binfmt (insn_t insn)
+{
+  const struct loongarch_opcode *it;
+  struct loongarch_ase *ase;
+  size_t i;
+  for (ase = loongarch_ASEs; ase->enabled; ase++)
+    {
+      if (!*ase->enabled || (ase->include && !*ase->include)
+	  || (ase->exclude && *ase->exclude))
+	continue;
+
+      if (!ase->opc_htab_inited)
+	{
+	  for (it = ase->opcodes; it->mask; it++)
+	    if (!ase->opc_htab[LARCH_INSN_OPC (it->match)]
+		&& it->macro == NULL)
+	      ase->opc_htab[LARCH_INSN_OPC (it->match)] = it;
+	  for (i = 0; i < 16; i++)
+	    if (!ase->opc_htab[i])
+	      ase->opc_htab[i] = it;
+	  ase->opc_htab_inited = 1;
+	}
+
+      it = ase->opc_htab[LARCH_INSN_OPC (insn)];
+      for (; it->name; it++)
+	if ((insn & it->mask) == it->match && it->mask
+	    && !(it->include && !*it->include)
+	    && !(it->exclude && *it->exclude))
+	  return it;
+    }
+  return NULL;
+}
+
+static const char *const *loongarch_r_disname = NULL;
+static const char *const *loongarch_f_disname = NULL;
+static const char *const *loongarch_c_disname = NULL;
+static const char *const *loongarch_cr_disname = NULL;
+static const char *const *loongarch_v_disname = NULL;
+static const char *const *loongarch_x_disname = NULL;
+
+static void
+set_default_loongarch_dis_options (void)
+{
+  LARCH_opts.ase_fix = 1;
+  LARCH_opts.ase_float = 1;
+  LARCH_opts.ase_128vec = 1;
+  LARCH_opts.ase_256vec = 1;
+
+  loongarch_r_disname = loongarch_r_lp64_name;
+  loongarch_f_disname = loongarch_f_lp64_name;
+  loongarch_c_disname = loongarch_c_normal_name;
+  loongarch_cr_disname = loongarch_cr_normal_name;
+  loongarch_v_disname = loongarch_v_normal_name;
+  loongarch_x_disname = loongarch_x_normal_name;
+}
+
+static int
+parse_loongarch_dis_option (const char *option)
+{
+  if (strcmp (option, "numeric") == 0)
+    {
+      loongarch_r_disname = loongarch_r_normal_name;
+      loongarch_f_disname = loongarch_f_normal_name;
+    }
+  return -1;
+}
+
+static int
+parse_loongarch_dis_options (const char *opts_in)
+{
+  set_default_loongarch_dis_options ();
+
+  if (opts_in == NULL)
+    return 0;
+
+  char *opts, *opt, *opt_end;
+  opts = xmalloc (strlen (opts_in) + 1);
+  strcpy (opts, opts_in);
+
+  for (opt = opt_end = opts; opt_end != NULL; opt = opt_end + 1)
+    {
+      if ((opt_end = strchr (opt, ',')) != NULL)
+	*opt_end = 0;
+      if (parse_loongarch_dis_option (opt) != 0)
+	return -1;
+    }
+  free (opts);
+  return 0;
+}
+
+static int32_t
+dis_one_arg (char esc1, char esc2, const char *bit_field,
+	     const char *arg ATTRIBUTE_UNUSED, void *context)
+{
+  static int need_comma = 0;
+  struct disassemble_info *info = context;
+  insn_t insn = *(insn_t *) info->private_data;
+  int32_t imm, u_imm;
+
+  if (esc1)
+    {
+      if (need_comma)
+	info->fprintf_func (info->stream, ", ");
+      need_comma = 1;
+      imm = loongarch_decode_imm (bit_field, insn, 1);
+      u_imm = loongarch_decode_imm (bit_field, insn, 0);
+    }
+
+  switch (esc1)
+    {
+    case 'r':
+      info->fprintf_func (info->stream, "%s", loongarch_r_disname[u_imm]);
+      break;
+    case 'f':
+      info->fprintf_func (info->stream, "%s", loongarch_f_disname[u_imm]);
+      break;
+    case 'c':
+      switch (esc2)
+	{
+	case 'r':
+	  info->fprintf_func (info->stream, "%s", loongarch_cr_disname[u_imm]);
+	  break;
+	default:
+	  info->fprintf_func (info->stream, "%s", loongarch_c_disname[u_imm]);
+	}
+      break;
+    case 'v':
+      info->fprintf_func (info->stream, "%s", loongarch_v_disname[u_imm]);
+      break;
+    case 'x':
+      info->fprintf_func (info->stream, "%s", loongarch_x_disname[u_imm]);
+      break;
+    case 'u':
+      info->fprintf_func (info->stream, "0x%x", u_imm);
+      break;
+    case 's':
+      if (imm == 0)
+	info->fprintf_func (info->stream, "%d", imm);
+      else
+	info->fprintf_func (info->stream, "%d(0x%x)", imm, u_imm);
+      switch (esc2)
+	{
+	case 'b':
+	  info->insn_type = dis_branch;
+	  info->target += imm;
+	}
+      break;
+    case '\0':
+      need_comma = 0;
+    }
+  return 0;
+}
+
+static void
+disassemble_one (insn_t insn, struct disassemble_info *info)
+{
+  const struct loongarch_opcode *opc = get_loongarch_opcode_by_binfmt (insn);
+
+#ifdef LOONGARCH_DEBUG
+  char have_space[32] = { 0 };
+  insn_t t;
+  int i;
+  const char *t_f = opc ? opc->format : NULL;
+  if (t_f)
+    while (*t_f)
+      {
+	while (('a' <= t_f[0] && t_f[0] <= 'z')
+	       || ('A' <= t_f[0] && t_f[0] <= 'Z')
+	       || t_f[0] == ',')
+	  t_f++;
+	while (1)
+	  {
+	    i = strtol (t_f, &t_f, 10);
+	    have_space[i] = 1;
+	    t_f++; /* ':' */
+	    i += strtol (t_f, &t_f, 10);
+	    have_space[i] = 1;
+	    if (t_f[0] == '|')
+	      t_f++;
+	    else
+	      break;
+	  }
+	if (t_f[0] == '<')
+	  t_f += 2; /* '<' '<' */
+	strtol (t_f, &t_f, 10);
+      }
+
+  have_space[28] = 1;
+  have_space[0] = 0;
+  t = ~((insn_t) -1 >> 1);
+  for (i = 31; 0 <= i; i--)
+    {
+      if (t & insn)
+	info->fprintf_func (info->stream, "1");
+      else
+	info->fprintf_func (info->stream, "0");
+      if (have_space[i])
+	info->fprintf_func (info->stream, " ");
+      t = t >> 1;
+    }
+  info->fprintf_func (info->stream, "\t");
+#endif
+
+  if (!opc)
+    {
+      info->insn_type = dis_noninsn;
+      info->fprintf_func (info->stream, "0x%08x", insn);
+      return;
+    }
+
+  info->insn_type = dis_nonbranch;
+  info->fprintf_func (info->stream, "%-12s", opc->name);
+
+  {
+    char *fake_args = xmalloc (strlen (opc->format) + 1);
+    const char *fake_arg_strs[MAX_ARG_NUM_PLUS_2];
+    strcpy (fake_args, opc->format);
+    if (0 < loongarch_split_args_by_comma (fake_args, fake_arg_strs))
+      info->fprintf_func (info->stream, "\t");
+    info->private_data = &insn;
+    loongarch_foreach_args (opc->format, fake_arg_strs, dis_one_arg, info);
+    free (fake_args);
+  }
+
+  if (info->insn_type == dis_branch || info->insn_type == dis_condbranch
+      /* Someother if we have extra info to print.  */)
+    info->fprintf_func (info->stream, "\t#");
+
+  if (info->insn_type == dis_branch || info->insn_type == dis_condbranch)
+    {
+      info->fprintf_func (info->stream, " ");
+      info->print_address_func (info->target, info);
+    }
+}
+
+int
+print_insn_loongarch (bfd_vma memaddr, struct disassemble_info *info)
+{
+  insn_t insn;
+  int status;
+
+  static int not_init_yet = 1;
+  if (not_init_yet)
+    {
+      parse_loongarch_dis_options (info->disassembler_options);
+      not_init_yet = 0;
+    }
+
+  info->bytes_per_chunk = 4;
+  info->bytes_per_line = 4;
+  info->display_endian = BFD_ENDIAN_LITTLE;
+  info->insn_info_valid = 1;
+  info->target = memaddr;
+
+  if ((status = info->read_memory_func (memaddr, (bfd_byte *) &insn,
+					sizeof (insn), info)) != 0)
+    {
+      info->memory_error_func (status, memaddr, info);
+      return -1; /* loongarch_insn_length (0); */
+    }
+
+  disassemble_one (insn, info);
+
+  return loongarch_insn_length (insn);
+}
+
+void
+print_loongarch_disassembler_options (FILE *stream)
+{
+  fprintf (stream, _ ("\n\
+The following LoongArch disassembler options are supported for use\n\
+with the -M switch (multiple options should be separated by commas):\n"));
+
+  fprintf (stream, _ ("\n\
+    numeric       Print numeric register names, rather than ABI names.\n"));
+  fprintf (stream, _ ("\n"));
+}
+
+int
+loongarch_parse_dis_options (const char *opts_in)
+{
+  return parse_loongarch_dis_options (opts_in);
+}
+
+static void
+my_print_address_func (bfd_vma addr, struct disassemble_info *dinfo)
+{
+  dinfo->fprintf_func (dinfo->stream, "0x%llx", (long long) addr);
+}
+
+void
+loongarch_disassemble_one (int64_t pc, insn_t insn,
+			   int (*fprintf_func) (void *stream,
+						const char *format, ...),
+			   void *stream)
+{
+  static struct disassemble_info my_disinfo =
+  {
+    .print_address_func = my_print_address_func,
+  };
+  static int not_init_yet = 1;
+  if (not_init_yet)
+    {
+      loongarch_parse_dis_options (NULL);
+      not_init_yet = 0;
+    }
+
+  my_disinfo.fprintf_func = fprintf_func;
+  my_disinfo.stream = stream;
+  my_disinfo.target = pc;
+  disassemble_one (insn, &my_disinfo);
+}
diff --git a/opcodes/loongarch-opc.c b/opcodes/loongarch-opc.c
new file mode 100644
index 00000000000..1e615fb22de
--- /dev/null
+++ b/opcodes/loongarch-opc.c
@@ -0,0 +1,782 @@
+/* LoongArch opcode support.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of the GNU opcodes library.
+
+   This library is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   It is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; see the file COPYING3.  If not,
+   see <http://www.gnu.org/licenses/>.  */
+
+#include <stddef.h>
+#include "opcode/loongarch.h"
+
+struct loongarch_ASEs_option LARCH_opts =
+{
+  .ase_fix = 0,
+  .ase_float = 0,
+  .ase_128vec = 0,
+  .ase_256vec = 0,
+
+  .addrwidth_is_32 = 0,
+  .addrwidth_is_64 = 0,
+  .rlen_is_32 = 0,
+  .rlen_is_64 = 0,
+  .la_local_with_abs = 0,
+  .la_global_with_pcrel = 0,
+  .la_global_with_abs = 0,
+
+  .abi_is_lp32 = 0,
+  .abi_is_lp64 = 0,
+};
+
+size_t
+loongarch_insn_length (insn_t insn)
+{
+  return insn ? 4 : 4; /* Eliminate warning.  */
+}
+
+const char *const loongarch_r_normal_name[32] =
+{
+  "$r0",  "$r1",  "$r2",  "$r3",  "$r4",  "$r5",  "$r6",  "$r7",
+  "$r8",  "$r9",  "$r10", "$r11", "$r12", "$r13", "$r14", "$r15",
+  "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23",
+  "$r24", "$r25", "$r26", "$r27", "$r28", "$r29", "$r30", "$r31",
+};
+
+const char *const loongarch_r_lp64_name[32] =
+{
+  "$zero", "$ra", "$tp", "$sp", "$a0", "$a1", "$a2", "$a3",
+  "$a4",   "$a5", "$a6", "$a7", "$t0", "$t1", "$t2", "$t3",
+  "$t4",   "$t5", "$t6", "$t7", "$t8", "$x",  "$fp", "$s0",
+  "$s1",   "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", "$s8",
+};
+
+const char *const loongarch_r_lp64_name1[32] =
+{
+  "", "", "", "", "$v0", "$v1", "", "", "", "", "", "", "", "", "", "",
+  "", "", "", "", "",    "",    "", "", "", "", "", "", "", "", "", "",
+};
+
+const char *const loongarch_f_normal_name[32] =
+{
+  "$f0",  "$f1",  "$f2",  "$f3",  "$f4",  "$f5",  "$f6",  "$f7",
+  "$f8",  "$f9",  "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
+  "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
+  "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
+};
+
+const char *const loongarch_f_lp64_name[32] =
+{
+  "$fa0", "$fa1", "$fa2",  "$fa3",  "$fa4",  "$fa5",  "$fa6",  "$fa7",
+  "$ft0", "$ft1", "$ft2",  "$ft3",  "$ft4",  "$ft5",  "$ft6",  "$ft7",
+  "$ft8", "$ft9", "$ft10", "$ft11", "$ft12", "$ft13", "$ft14", "$ft15",
+  "$fs0", "$fs1", "$fs2",  "$fs3",  "$fs4",  "$fs5",  "$fs6",  "$fs7",
+};
+
+const char *const loongarch_f_lp64_name1[32] =
+{
+  "$fv0", "$fv1", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+  "",     "",     "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+};
+
+const char *const loongarch_c_normal_name[8] =
+{
+  "$fcc0", "$fcc1", "$fcc2", "$fcc3", "$fcc4", "$fcc5", "$fcc6", "$fcc7",
+};
+
+const char *const loongarch_cr_normal_name[4] =
+{
+  "$scr0",
+  "$scr1",
+  "$scr2",
+  "$scr3",
+};
+
+const char *const loongarch_v_normal_name[32] =
+{
+  "$vr0",  "$vr1",  "$vr2",  "$vr3",  "$vr4",  "$vr5",  "$vr6",  "$vr7",
+  "$vr8",  "$vr9",  "$vr10", "$vr11", "$vr12", "$vr13", "$vr14", "$vr15",
+  "$vr16", "$vr17", "$vr18", "$vr19", "$vr20", "$vr21", "$vr22", "$vr23",
+  "$vr24", "$vr25", "$vr26", "$vr27", "$vr28", "$vr29", "$vr30", "$vr31",
+};
+
+const char *const loongarch_x_normal_name[32] =
+{
+  "$xr0",  "$xr1",  "$xr2",  "$xr3",  "$xr4",  "$xr5",  "$xr6",  "$xr7",
+  "$xr8",  "$xr9",  "$xr10", "$xr11", "$xr12", "$xr13", "$xr14", "$xr15",
+  "$xr16", "$xr17", "$xr18", "$xr19", "$xr20", "$xr21", "$xr22", "$xr23",
+  "$xr24", "$xr25", "$xr26", "$xr27", "$xr28", "$xr29", "$xr30", "$xr31",
+};
+
+static struct loongarch_opcode loongarch_macro_opcodes[] =
+{
+  /* match,    mask,       name, format, macro, include, exclude, pinfo.  */
+  { 0, 0, "li.w", "r,sc", "%f", 0, 0, 0},
+  { 0, 0, "li.d", "r,sc", "%f", 0, 0, 0},
+  { 0, 0, "la", "r,la", "la.global %1,%2", 0, 0, 0 },
+
+  { 0, 0, "la.global", "r,la", "la.pcrel %1,%2",
+    &LARCH_opts.la_global_with_pcrel, 0, 0 },
+  { 0, 0, "la.global", "r,r,la", "la.pcrel %1,%2,%3",
+    &LARCH_opts.la_global_with_pcrel, 0, 0 },
+  { 0, 0, "la.global", "r,la", "la.abs %1,%2", &LARCH_opts.la_global_with_abs,
+    0, 0 },
+  { 0, 0, "la.global", "r,r,la", "la.abs %1,%3",
+    &LARCH_opts.la_global_with_abs, 0, 0 },
+  { 0, 0, "la.global", "r,l", "la.got %1,%2", 0, 0, 0 },
+  { 0, 0, "la.global", "r,r,l", "la.got %1,%2,%3", 0, 0, 0 },
+
+  { 0, 0, "la.local", "r,la", "la.abs %1,%2", &LARCH_opts.la_local_with_abs, 0,
+    0 },
+  { 0, 0, "la.local", "r,r,la", "la.abs %1,%3", &LARCH_opts.la_local_with_abs,
+    0, 0 },
+  { 0, 0, "la.local", "r,la", "la.pcrel %1,%2", 0, 0, 0 },
+  { 0, 0, "la.local", "r,r,la", "la.pcrel %1,%2,%3", 0, 0, 0 },
+
+  { 0, 0, "la.abs", "r,la",
+    "lu12i.w %1,%%abs(%2)>>12;"
+    "ori %1,%1,%%abs(%2)&0xfff;",
+    &LARCH_opts.addrwidth_is_32, 0, 0 },
+  { 0, 0, "la.abs", "r,la",
+    "lu12i.w %1,%%abs(%2)<<32>>44;"
+    "ori %1,%1,%%abs(%2)&0xfff;"
+    "lu32i.d %1,%%abs(%2)<<12>>44;"
+    "lu52i.d %1,%1,%%abs(%2)>>52;",
+    &LARCH_opts.addrwidth_is_64, 0, 0 },
+
+  { 0, 0, "la.pcrel", "r,la",
+    "pcaddu12i %1,%%pcrel(%2+0x800)<<32>>44;"
+    "addi.w %1,%1,%%pcrel(%2+4)-(%%pcrel(%2+4+0x800)>>12<<12);",
+    &LARCH_opts.addrwidth_is_32, 0, 0 },
+
+  { 0, 0, "la.pcrel", "r,la",
+    "pcaddu12i %1,%%pcrel(%2+0x800)>>12;"
+    "addi.d %1,%1,%%pcrel(%2+4)-(%%pcrel(%2+4+0x800)>>12<<12);",
+    &LARCH_opts.addrwidth_is_64, 0, 0 },
+  { 0, 0, "la.pcrel", "r,r,la",
+    "pcaddu12i %1,(%%pcrel(%3)-(%%pcrel(%3+0x80000000)>>32<<32))<<32>>44;"
+    "ori %2,$r0,(%%pcrel(%3+4)-(%%pcrel(%3+4+0x80000000)>>32<<32))&0xfff;"
+    "lu32i.d %2,%%pcrel(%3+8+0x80000000)<<12>>44;"
+    "lu52i.d %2,%2,%%pcrel(%3+12+0x80000000)>>52;"
+    "add.d %1,%1,%2;",
+    &LARCH_opts.addrwidth_is_64, 0, 0 },
+
+  { 0, 0, "la.got", "r,l",
+    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%gprel(%2))<<32>>44;"
+    "ld.w "
+    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%gprel(%2)-((%%pcrel(_GLOBAL_"
+    "OFFSET_TABLE_+4+0x800)+%%gprel(%2))>>12<<12);",
+    &LARCH_opts.addrwidth_is_32, 0, 0 },
+
+  { 0, 0, "la.got", "r,l",
+    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%gprel(%2))>>12;"
+    "ld.d "
+    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%gprel(%2)-((%%pcrel(_GLOBAL_"
+    "OFFSET_TABLE_+4+0x800)+%%gprel(%2))>>12<<12);",
+    &LARCH_opts.addrwidth_is_64, 0, 0 },
+  { 0, 0, "la.got", "r,r,l",
+    "pcaddu12i "
+    "%1,(%%pcrel(_GLOBAL_OFFSET_TABLE_)+%%gprel(%3)-((%%pcrel(_GLOBAL_OFFSET_"
+    "TABLE_+0x80000000)+%%gprel(%3))>>32<<32))<<32>>44;"
+    "ori "
+    "%2,$r0,(%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%gprel(%3)-((%%pcrel(_GLOBAL_"
+    "OFFSET_TABLE_+4+0x80000000)+%%gprel(%3))>>32<<32))&0xfff;"
+    "lu32i.d "
+    "%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+8+0x80000000)+%%gprel(%3))<<12>>44;"
+    "lu52i.d "
+    "%2,%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+12+0x80000000)+%%gprel(%3))>>52;"
+    "ldx.d %1,%1,%2;",
+    &LARCH_opts.addrwidth_is_64, 0, 0 },
+
+  { 0, 0, "la.tls.le", "r,la",
+    "lu12i.w %1,%%tprel(%2)>>12;"
+    "ori %1,%1,%%tprel(%2)&0xfff",
+    &LARCH_opts.addrwidth_is_32, 0, 0 },
+  /* { 0, 0, "la.tls.le", "r,la",
+  * "lu12i.w %1,%%tprel(%2)>>12;"
+  * "ori %1,%1,%%tprel(%2)&0xfff"
+  * , &LARCH_opts.addrwidth_is_64, 0, 0}, */
+  { 0, 0, "la.tls.le", "r,la",
+    "lu12i.w %1,%%tprel(%2)<<32>>44;"
+    "ori %1,%1,%%tprel(%2)&0xfff;"
+    "lu32i.d %1,%%tprel(%2)<<12>>44;"
+    "lu52i.d %1,%1,%%tprel(%2)>>52;",
+    &LARCH_opts.addrwidth_is_64, 0, 0 },
+
+  { 0, 0, "la.tls.ie", "r,l",
+    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%tlsgot(%2))<<32>>44;"
+    "ld.w "
+    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgot(%2)-((%%pcrel(_GLOBAL_"
+    "OFFSET_TABLE_+4+0x800)+%%tlsgot(%2))>>12<<12);",
+    &LARCH_opts.addrwidth_is_32, 0, 0 },
+
+  { 0, 0, "la.tls.ie", "r,l",
+    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%tlsgot(%2))>>12;"
+    "ld.d "
+    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgot(%2)-((%%pcrel(_GLOBAL_"
+    "OFFSET_TABLE_+4+0x800)+%%tlsgot(%2))>>12<<12);",
+    &LARCH_opts.addrwidth_is_64, 0, 0 },
+  { 0, 0, "la.tls.ie", "r,r,l",
+    "pcaddu12i "
+    "%1,(%%pcrel(_GLOBAL_OFFSET_TABLE_)+%%tlsgot(%3)-((%%pcrel(_GLOBAL_OFFSET_"
+    "TABLE_+0x80000000)+%%tlsgot(%3))>>32<<32))<<32>>44;"
+    "ori "
+    "%2,$r0,(%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgot(%3)-((%%pcrel(_GLOBAL_"
+    "OFFSET_TABLE_+4+0x80000000)+%%tlsgot(%3))>>32<<32))&0xfff;"
+    "lu32i.d "
+    "%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+8+0x80000000)+%%tlsgot(%3))<<12>>44;"
+    "lu52i.d "
+    "%2,%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+12+0x80000000)+%%tlsgot(%3))>>52;"
+    "ldx.d %1,%1,%2;",
+    &LARCH_opts.addrwidth_is_64, 0, 0 },
+
+  { 0, 0, "la.tls.ld", "r,l", "la.tls.gd %1,%2", 0, 0, 0 },
+  { 0, 0, "la.tls.ld", "r,r,l", "la.tls.gd %1,%2,%3",
+    &LARCH_opts.addrwidth_is_64, 0, 0 },
+
+  { 0, 0, "la.tls.gd", "r,l",
+    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%tlsgd(%2))<<32>>44;"
+    "addi.w "
+    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgd(%2)-((%%pcrel(_GLOBAL_"
+    "OFFSET_TABLE_+4+0x800)+%%tlsgd(%2))>>12<<12);",
+    &LARCH_opts.addrwidth_is_32, 0, 0 },
+
+  { 0, 0, "la.tls.gd", "r,l",
+    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%tlsgd(%2))>>12;"
+    "addi.d "
+    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgd(%2)-((%%pcrel(_GLOBAL_"
+    "OFFSET_TABLE_+4+0x800)+%%tlsgd(%2))>>12<<12);",
+    &LARCH_opts.addrwidth_is_64, 0, 0 },
+  { 0, 0, "la.tls.gd", "r,r,l",
+    "pcaddu12i "
+    "%1,(%%pcrel(_GLOBAL_OFFSET_TABLE_)+%%tlsgd(%3)-((%%pcrel(_GLOBAL_OFFSET_"
+    "TABLE_+0x80000000)+%%tlsgd(%3))>>32<<32))<<32>>44;"
+    "ori "
+    "%2,$r0,(%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgd(%3)-((%%pcrel(_GLOBAL_"
+    "OFFSET_TABLE_+4+0x80000000)+%%tlsgd(%3))>>32<<32))&0xfff;"
+    "lu32i.d "
+    "%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+8+0x80000000)+%%tlsgd(%3))<<12>>44;"
+    "lu52i.d "
+    "%2,%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+12+0x80000000)+%%tlsgd(%3))>>52;"
+    "add.d %1,%1,%2;",
+    &LARCH_opts.addrwidth_is_64, 0, 0 },
+
+  { 0 } /* Terminate the list.  */
+};
+
+static struct loongarch_opcode loongarch_fix_opcodes[] =
+{
+  /* match,	mask,		name,		format,				macro,			include, exclude, pinfo.  */
+  { 0x00001000, 0xfffffc00,	"clo.w",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00001400, 0xfffffc00,	"clz.w",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00001800, 0xfffffc00,	"cto.w",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00001c00, 0xfffffc00,	"ctz.w",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00002000, 0xfffffc00,	"clo.d",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00002400, 0xfffffc00,	"clz.d",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00002800, 0xfffffc00,	"cto.d",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00002c00, 0xfffffc00,	"ctz.d",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00003000, 0xfffffc00,	"revb.2h",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00003400, 0xfffffc00,	"revb.4h",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00003800, 0xfffffc00,	"revb.2w",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00003c00, 0xfffffc00,	"revb.d",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00004000, 0xfffffc00,	"revh.2w",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00004400, 0xfffffc00,	"revh.d",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00004800, 0xfffffc00,	"bitrev.4b",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00004c00, 0xfffffc00,	"bitrev.8b",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00005000, 0xfffffc00,	"bitrev.w",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00005400, 0xfffffc00,	"bitrev.d",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00005800, 0xfffffc00,	"ext.w.h",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00005c00, 0xfffffc00,	"ext.w.b",	"r0:5,r5:5",			0,			0,	0,	0 },
+  /* or %1,%2,$r0  */
+  { 0x00150000, 0xfffffc00,	"move",		"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00006000, 0xfffffc00,	"rdtimel.w",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00006400, 0xfffffc00,	"rdtimeh.w",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00006800, 0xfffffc00,	"rdtime.d",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00006c00, 0xfffffc00,	"cpucfg",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x00010000, 0xffff801f,	"asrtle.d",	"r5:5,r10:5",			0,			0,	0,	0 },
+  { 0x00018000, 0xffff801f,	"asrtgt.d",	"r5:5,r10:5",			0,			0,	0,	0 },
+  { 0x00040000, 0xfffe0000,	"alsl.w",	"r0:5,r5:5,r10:5,u15:2+1",	0,			0,	0,	0 },
+  { 0x00060000, 0xfffe0000,	"alsl.wu",	"r0:5,r5:5,r10:5,u15:2+1",	0,			0,	0,	0 },
+  { 0x00080000, 0xfffe0000,	"bytepick.w",	"r0:5,r5:5,r10:5,u15:2",	0,			0,	0,	0 },
+  { 0x000c0000, 0xfffc0000,	"bytepick.d",	"r0:5,r5:5,r10:5,u15:3",	0,			0,	0,	0 },
+  { 0x00100000, 0xffff8000,	"add.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00108000, 0xffff8000,	"add.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00110000, 0xffff8000,	"sub.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00118000, 0xffff8000,	"sub.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00120000, 0xffff8000,	"slt",		"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00128000, 0xffff8000,	"sltu",		"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00130000, 0xffff8000,	"maskeqz",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00138000, 0xffff8000,	"masknez",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00140000, 0xffff8000,	"nor",		"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00148000, 0xffff8000,	"and",		"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00150000, 0xffff8000,	"or",		"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00158000, 0xffff8000,	"xor",		"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00160000, 0xffff8000,	"orn",		"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00168000, 0xffff8000,	"andn",		"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00170000, 0xffff8000,	"sll.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00178000, 0xffff8000,	"srl.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00180000, 0xffff8000,	"sra.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00188000, 0xffff8000,	"sll.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00190000, 0xffff8000,	"srl.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00198000, 0xffff8000,	"sra.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x001b0000, 0xffff8000,	"rotr.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x001b8000, 0xffff8000,	"rotr.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x001c0000, 0xffff8000,	"mul.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x001c8000, 0xffff8000,	"mulh.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x001d0000, 0xffff8000,	"mulh.wu",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x001d8000, 0xffff8000,	"mul.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x001e0000, 0xffff8000,	"mulh.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x001e8000, 0xffff8000,	"mulh.du",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x001f0000, 0xffff8000,	"mulw.d.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x001f8000, 0xffff8000,	"mulw.d.wu",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00200000, 0xffff8000,	"div.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00208000, 0xffff8000,	"mod.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00210000, 0xffff8000,	"div.wu",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00218000, 0xffff8000,	"mod.wu",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00220000, 0xffff8000,	"div.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00228000, 0xffff8000,	"mod.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00230000, 0xffff8000,	"div.du",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00238000, 0xffff8000,	"mod.du",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00240000, 0xffff8000,	"crc.w.b.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00248000, 0xffff8000,	"crc.w.h.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00250000, 0xffff8000,	"crc.w.w.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00258000, 0xffff8000,	"crc.w.d.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00260000, 0xffff8000,	"crcc.w.b.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00268000, 0xffff8000,	"crcc.w.h.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00270000, 0xffff8000,	"crcc.w.w.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x00278000, 0xffff8000,	"crcc.w.d.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x002a0000, 0xffff8000,	"break",	"u0:15",			0,			0,	0,	0 },
+  { 0x002a8000, 0xffff8000,	"dbcl",		"u0:15",			0,			0,	0,	0 },
+  { 0x002b0000, 0xffff8000,	"syscall",	"u0:15",			0,			0,	0,	0 },
+  { 0x002c0000, 0xfffe0000,	"alsl.d",	"r0:5,r5:5,r10:5,u15:2+1",	0,			0,	0,	0 },
+  { 0x00408000, 0xffff8000,	"slli.w",	"r0:5,r5:5,u10:5",		0,			0,	0,	0 },
+  { 0x00410000, 0xffff0000,	"slli.d",	"r0:5,r5:5,u10:6",		0,			0,	0,	0 },
+  { 0x00448000, 0xffff8000,	"srli.w",	"r0:5,r5:5,u10:5",		0,			0,	0,	0 },
+  { 0x00450000, 0xffff0000,	"srli.d",	"r0:5,r5:5,u10:6",		0,			0,	0,	0 },
+  { 0x00488000, 0xffff8000,	"srai.w",	"r0:5,r5:5,u10:5",		0,			0,	0,	0 },
+  { 0x00490000, 0xffff0000,	"srai.d",	"r0:5,r5:5,u10:6",		0,			0,	0,	0 },
+  { 0x004c8000, 0xffff8000,	"rotri.w",	"r0:5,r5:5,u10:5",		0,			0,	0,	0 },
+  { 0x004d0000, 0xffff0000,	"rotri.d",	"r0:5,r5:5,u10:6",		0,			0,	0,	0 },
+  { 0x00600000, 0xffe08000,	"bstrins.w",	"r0:5,r5:5,u16:5,u10:5",	0,			0,	0,	0 },
+  { 0x00608000, 0xffe08000,	"bstrpick.w",	"r0:5,r5:5,u16:5,u10:5",	0,			0,	0,	0 },
+  { 0x00800000, 0xffc00000,	"bstrins.d",	"r0:5,r5:5,u16:6,u10:6",	0,			0,	0,	0 },
+  { 0x00c00000, 0xffc00000,	"bstrpick.d",	"r0:5,r5:5,u16:6,u10:6",	0,			0,	0,	0 },
+  { 0 } /* Terminate the list.  */
+};
+
+static struct loongarch_opcode loongarch_float_opcodes[] =
+{
+  /* match,	mask,		name,		format,				macro,			include, exclude, pinfo.  */
+  { 0x01008000, 0xffff8000,	"fadd.s",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01010000, 0xffff8000,	"fadd.d",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01028000, 0xffff8000,	"fsub.s",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01030000, 0xffff8000,	"fsub.d",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01048000, 0xffff8000,	"fmul.s",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01050000, 0xffff8000,	"fmul.d",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01068000, 0xffff8000,	"fdiv.s",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01070000, 0xffff8000,	"fdiv.d",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01088000, 0xffff8000,	"fmax.s",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01090000, 0xffff8000,	"fmax.d",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x010a8000, 0xffff8000,	"fmin.s",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x010b0000, 0xffff8000,	"fmin.d",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x010c8000, 0xffff8000,	"fmaxa.s",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x010d0000, 0xffff8000,	"fmaxa.d",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x010e8000, 0xffff8000,	"fmina.s",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x010f0000, 0xffff8000,	"fmina.d",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01108000, 0xffff8000,	"fscaleb.s",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01110000, 0xffff8000,	"fscaleb.d",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01128000, 0xffff8000,	"fcopysign.s",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01130000, 0xffff8000,	"fcopysign.d",	"f0:5,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x01140400, 0xfffffc00,	"fabs.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01140800, 0xfffffc00,	"fabs.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01141400, 0xfffffc00,	"fneg.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01141800, 0xfffffc00,	"fneg.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01142400, 0xfffffc00,	"flogb.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01142800, 0xfffffc00,	"flogb.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01143400, 0xfffffc00,	"fclass.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01143800, 0xfffffc00,	"fclass.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01144400, 0xfffffc00,	"fsqrt.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01144800, 0xfffffc00,	"fsqrt.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01145400, 0xfffffc00,	"frecip.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01145800, 0xfffffc00,	"frecip.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01146400, 0xfffffc00,	"frsqrt.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01146800, 0xfffffc00,	"frsqrt.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01149400, 0xfffffc00,	"fmov.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01149800, 0xfffffc00,	"fmov.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x0114a400, 0xfffffc00,	"movgr2fr.w",	"f0:5,r5:5",			0,			0,	0,	0 },
+  { 0x0114a800, 0xfffffc00,	"movgr2fr.d",	"f0:5,r5:5",			0,			0,	0,	0 },
+  { 0x0114ac00, 0xfffffc00,	"movgr2frh.w",	"f0:5,r5:5",			0,			0,	0,	0 },
+  { 0x0114b400, 0xfffffc00,	"movfr2gr.s",	"r0:5,f5:5",			0,			0,	0,	0 },
+  { 0x0114b800, 0xfffffc00,	"movfr2gr.d",	"r0:5,f5:5",			0,			0,	0,	0 },
+  { 0x0114bc00, 0xfffffc00,	"movfrh2gr.s",	"r0:5,f5:5",			0,			0,	0,	0 },
+  { 0x0114c000, 0xfffffc00,	"movgr2fcsr",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x0114c800, 0xfffffc00,	"movfcsr2gr",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x0114d000, 0xfffffc18,	"movfr2cf",	"c0:3,f5:5",			0,			0,	0,	0 },
+  { 0x0114d400, 0xffffff00,	"movcf2fr",	"f0:5,c5:3",			0,			0,	0,	0 },
+  { 0x0114d800, 0xfffffc18,	"movgr2cf",	"c0:3,r5:5",			0,			0,	0,	0 },
+  { 0x0114dc00, 0xffffff00,	"movcf2gr",	"r0:5,c5:3",			0,			0,	0,	0 },
+  { 0x01191800, 0xfffffc00,	"fcvt.s.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x01192400, 0xfffffc00,	"fcvt.d.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011a0400, 0xfffffc00,	"ftintrm.w.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011a0800, 0xfffffc00,	"ftintrm.w.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011a2400, 0xfffffc00,	"ftintrm.l.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011a2800, 0xfffffc00,	"ftintrm.l.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011a4400, 0xfffffc00,	"ftintrp.w.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011a4800, 0xfffffc00,	"ftintrp.w.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011a6400, 0xfffffc00,	"ftintrp.l.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011a6800, 0xfffffc00,	"ftintrp.l.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011a8400, 0xfffffc00,	"ftintrz.w.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011a8800, 0xfffffc00,	"ftintrz.w.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011aa400, 0xfffffc00,	"ftintrz.l.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011aa800, 0xfffffc00,	"ftintrz.l.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011ac400, 0xfffffc00,	"ftintrne.w.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011ac800, 0xfffffc00,	"ftintrne.w.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011ae400, 0xfffffc00,	"ftintrne.l.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011ae800, 0xfffffc00,	"ftintrne.l.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011b0400, 0xfffffc00,	"ftint.w.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011b0800, 0xfffffc00,	"ftint.w.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011b2400, 0xfffffc00,	"ftint.l.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011b2800, 0xfffffc00,	"ftint.l.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011d1000, 0xfffffc00,	"ffint.s.w",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011d1800, 0xfffffc00,	"ffint.s.l",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011d2000, 0xfffffc00,	"ffint.d.w",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011d2800, 0xfffffc00,	"ffint.d.l",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011e4400, 0xfffffc00,	"frint.s",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0x011e4800, 0xfffffc00,	"frint.d",	"f0:5,f5:5",			0,			0,	0,	0 },
+  { 0 } /* Terminate the list.  */
+};
+
+static struct loongarch_opcode loongarch_lmm_opcodes[] =
+{
+  /* match,	mask,		name,		format,				macro,			include, exclude, pinfo.  */
+  { 0x02000000, 0xffc00000,	"slti",		"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x02400000, 0xffc00000,	"sltui",	"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x02800000, 0xffc00000,	"addi.w",	"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x02c00000, 0xffc00000,	"addi.d",	"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x03000000, 0xffc00000,	"lu52i.d",	"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"nop",		"",				"andi $r0,$r0,0",	0,	0,	0 },
+  { 0x03400000, 0xffc00000,	"andi",		"r0:5,r5:5,u10:12",		0,			0,	0,	0 },
+  { 0x03800000, 0xffc00000,	"ori",		"r0:5,r5:5,u10:12",		0,			0,	0,	0 },
+  { 0x03c00000, 0xffc00000,	"xori",		"r0:5,r5:5,u10:12",		0,			0,	0,	0 },
+  { 0x10000000, 0xfc000000,	"addu16i.d",	"r0:5,r5:5,s10:16",		0,			0,	0,	0 },
+  { 0x14000000, 0xfe000000,	"lu12i.w",	"r0:5,s5:20",			0,			0,	0,	0 },
+  { 0x16000000, 0xfe000000,	"lu32i.d",	"r0:5,s5:20",			0,			0,	0,	0 },
+  { 0x18000000, 0xfe000000,	"pcaddi",	"r0:5,s5:20",			0,			0,	0,	0 },
+  { 0x1a000000, 0xfe000000,	"pcalau12i",	"r0:5,s5:20",			0,			0,	0,	0 },
+  { 0x1c000000, 0xfe000000,	"pcaddu12i",	"r0:5,s5:20",			0,			0,	0,	0 },
+  { 0x1e000000, 0xfe000000,	"pcaddu18i",	"r0:5,s5:20",			0,			0,	0,	0 },
+  { 0 } /* Terminate the list.  */
+};
+
+static struct loongarch_opcode loongarch_privilege_opcodes[] =
+{
+  /* match,	mask,		name,		format,				macro,			include, exclude, pinfo.  */
+  { 0x04000000, 0xff0003e0,	"csrrd",	"r0:5,u10:14",			0,			0,	0,	0 },
+  { 0x04000020, 0xff0003e0,	"csrwr",	"r0:5,u10:14",			0,			0,	0,	0 },
+  { 0x04000000, 0xff000000,	"csrxchg",	"r0:5,r5:5,u10:14",		0,			0,	0,	0 },
+  { 0x06000000, 0xffc00000,	"cacop",	"u0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x06400000, 0xfffc0000,	"lddir",	"r0:5,r5:5,u10:8",		0,			0,	0,	0 },
+  { 0x06440000, 0xfffc001f,	"ldpte",	"r5:5,u10:8",			0,			0,	0,	0 },
+  { 0x06480000, 0xfffffc00,	"iocsrrd.b",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x06480400, 0xfffffc00,	"iocsrrd.h",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x06480800, 0xfffffc00,	"iocsrrd.w",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x06480c00, 0xfffffc00,	"iocsrrd.d",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x06481000, 0xfffffc00,	"iocsrwr.b",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x06481400, 0xfffffc00,	"iocsrwr.h",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x06481800, 0xfffffc00,	"iocsrwr.w",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x06481c00, 0xfffffc00,	"iocsrwr.d",	"r0:5,r5:5",			0,			0,	0,	0 },
+  { 0x06482000, 0xffffffff,	"tlbclr",	"",				0,			0,	0,	0 },
+  { 0x06482400, 0xffffffff,	"tlbflush",	"",				0,			0,	0,	0 },
+  { 0x06482800, 0xffffffff,	"tlbsrch",	"",				0,			0,	0,	0 },
+  { 0x06482c00, 0xffffffff,	"tlbrd",	"",				0,			0,	0,	0 },
+  { 0x06483000, 0xffffffff,	"tlbwr",	"",				0,			0,	0,	0 },
+  { 0x06483400, 0xffffffff,	"tlbfill",	"",				0,			0,	0,	0 },
+  { 0x06483800, 0xffffffff,	"ertn",		"",				0,			0,	0,	0 },
+  { 0x06488000, 0xffff8000,	"idle",		"u0:15",			0,			0,	0,	0 },
+  { 0x06498000, 0xffff8000,	"invtlb",	"u0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0 } /* Terminate the list.  */
+};
+
+static struct loongarch_opcode loongarch_4opt_opcodes[] =
+{
+  /* match,	mask,		name,		format,				macro,			include, exclude, pinfo.  */
+  { 0x08100000, 0xfff00000,	"fmadd.s",	"f0:5,f5:5,f10:5,f15:5",	0,			0,	0,	0 },
+  { 0x08200000, 0xfff00000,	"fmadd.d",	"f0:5,f5:5,f10:5,f15:5",	0,			0,	0,	0 },
+  { 0x08500000, 0xfff00000,	"fmsub.s",	"f0:5,f5:5,f10:5,f15:5",	0,			0,	0,	0 },
+  { 0x08600000, 0xfff00000,	"fmsub.d",	"f0:5,f5:5,f10:5,f15:5",	0,			0,	0,	0 },
+  { 0x08900000, 0xfff00000,	"fnmadd.s",	"f0:5,f5:5,f10:5,f15:5",	0,			0,	0,	0 },
+  { 0x08a00000, 0xfff00000,	"fnmadd.d",	"f0:5,f5:5,f10:5,f15:5",	0,			0,	0,	0 },
+  { 0x08d00000, 0xfff00000,	"fnmsub.s",	"f0:5,f5:5,f10:5,f15:5",	0,			0,	0,	0 },
+  { 0x08e00000, 0xfff00000,	"fnmsub.d",	"f0:5,f5:5,f10:5,f15:5",	0,			0,	0,	0 },
+  { 0x0c100000, 0xffff8018,	"fcmp.caf.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c108000, 0xffff8018,	"fcmp.saf.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c110000, 0xffff8018,	"fcmp.clt.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c118000, 0xffff8018,	"fcmp.slt.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c118000, 0xffff8018,	"fcmp.sgt.s",	"c0:3,f10:5,f5:5",		0,			0,	0,	0 },
+  { 0x0c120000, 0xffff8018,	"fcmp.ceq.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c128000, 0xffff8018,	"fcmp.seq.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c130000, 0xffff8018,	"fcmp.cle.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c138000, 0xffff8018,	"fcmp.sle.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c138000, 0xffff8018,	"fcmp.sge.s",	"c0:3,f10:5,f5:5",		0,			0,	0,	0 },
+  { 0x0c140000, 0xffff8018,	"fcmp.cun.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c148000, 0xffff8018,	"fcmp.sun.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c150000, 0xffff8018,	"fcmp.cult.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c150000, 0xffff8018,	"fcmp.cugt.s",	"c0:3,f10:5,f5:5",		0,			0,	0,	0 },
+  { 0x0c158000, 0xffff8018,	"fcmp.sult.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c160000, 0xffff8018,	"fcmp.cueq.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c168000, 0xffff8018,	"fcmp.sueq.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c170000, 0xffff8018,	"fcmp.cule.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c170000, 0xffff8018,	"fcmp.cuge.s",	"c0:3,f10:5,f5:5",		0,			0,	0,	0 },
+  { 0x0c178000, 0xffff8018,	"fcmp.sule.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c180000, 0xffff8018,	"fcmp.cne.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c188000, 0xffff8018,	"fcmp.sne.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c1a0000, 0xffff8018,	"fcmp.cor.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c1a8000, 0xffff8018,	"fcmp.sor.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c1c0000, 0xffff8018,	"fcmp.cune.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c1c8000, 0xffff8018,	"fcmp.sune.s",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c200000, 0xffff8018,	"fcmp.caf.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c208000, 0xffff8018,	"fcmp.saf.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c210000, 0xffff8018,	"fcmp.clt.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c218000, 0xffff8018,	"fcmp.slt.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c218000, 0xffff8018,	"fcmp.sgt.d",	"c0:3,f10:5,f5:5",		0,			0,	0,	0 },
+  { 0x0c220000, 0xffff8018,	"fcmp.ceq.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c228000, 0xffff8018,	"fcmp.seq.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c230000, 0xffff8018,	"fcmp.cle.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c238000, 0xffff8018,	"fcmp.sle.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c238000, 0xffff8018,	"fcmp.sge.d",	"c0:3,f10:5,f5:5",		0,			0,	0,	0 },
+  { 0x0c240000, 0xffff8018,	"fcmp.cun.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c248000, 0xffff8018,	"fcmp.sun.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c250000, 0xffff8018,	"fcmp.cult.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c250000, 0xffff8018,	"fcmp.cugt.d",	"c0:3,f10:5,f5:5",		0,			0,	0,	0 },
+  { 0x0c258000, 0xffff8018,	"fcmp.sult.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c260000, 0xffff8018,	"fcmp.cueq.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c268000, 0xffff8018,	"fcmp.sueq.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c270000, 0xffff8018,	"fcmp.cule.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c270000, 0xffff8018,	"fcmp.cuge.d",	"c0:3,f10:5,f5:5",		0,			0,	0,	0 },
+  { 0x0c278000, 0xffff8018,	"fcmp.sule.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c280000, 0xffff8018,	"fcmp.cne.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c288000, 0xffff8018,	"fcmp.sne.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c2a0000, 0xffff8018,	"fcmp.cor.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c2a8000, 0xffff8018,	"fcmp.sor.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c2c0000, 0xffff8018,	"fcmp.cune.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0c2c8000, 0xffff8018,	"fcmp.sune.d",	"c0:3,f5:5,f10:5",		0,			0,	0,	0 },
+  { 0x0d000000, 0xfffc0000,	"fsel",		"f0:5,f5:5,f10:5,c15:3",	0,			0,	0,	0 },
+  { 0 } /* Terminate the list.  */
+};
+
+static struct loongarch_opcode loongarch_load_store_opcodes[] =
+{
+  /* match,	mask,		name,		format,				macro,			include, exclude, pinfo.  */
+  { 0x20000000, 0xff000000,	"ll.w",		"r0:5,r5:5,s10:14<<2",		0,			0,	0,	0 },
+  { 0x21000000, 0xff000000,	"sc.w",		"r0:5,r5:5,s10:14<<2",		0,			0,	0,	0 },
+  { 0x22000000, 0xff000000,	"ll.d",		"r0:5,r5:5,s10:14<<2",		0,			0,	0,	0 },
+  { 0x23000000, 0xff000000,	"sc.d",		"r0:5,r5:5,s10:14<<2",		0,			0,	0,	0 },
+  { 0x24000000, 0xff000000,	"ldptr.w",	"r0:5,r5:5,s10:14<<2",		0,			0,	0,	0 },
+  { 0x25000000, 0xff000000,	"stptr.w",	"r0:5,r5:5,s10:14<<2",		0,			0,	0,	0 },
+  { 0x26000000, 0xff000000,	"ldptr.d",	"r0:5,r5:5,s10:14<<2",		0,			0,	0,	0 },
+  { 0x27000000, 0xff000000,	"stptr.d",	"r0:5,r5:5,s10:14<<2",		0,			0,	0,	0 },
+  { 0x28000000, 0xffc00000,	"ld.b",		"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x28400000, 0xffc00000,	"ld.h",		"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x28800000, 0xffc00000,	"ld.w",		"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x28c00000, 0xffc00000,	"ld.d",		"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x29000000, 0xffc00000,	"st.b",		"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x29400000, 0xffc00000,	"st.h",		"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x29800000, 0xffc00000,	"st.w",		"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x29c00000, 0xffc00000,	"st.d",		"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x2a000000, 0xffc00000,	"ld.bu",	"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x2a400000, 0xffc00000,	"ld.hu",	"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x2a800000, 0xffc00000,	"ld.wu",	"r0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x2ac00000, 0xffc00000,	"preld",	"u0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x2b000000, 0xffc00000,	"fld.s",	"f0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x2b400000, 0xffc00000,	"fst.s",	"f0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x2b800000, 0xffc00000,	"fld.d",	"f0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x2bc00000, 0xffc00000,	"fst.d",	"f0:5,r5:5,s10:12",		0,			0,	0,	0 },
+  { 0x38000000, 0xffff8000,	"ldx.b",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38040000, 0xffff8000,	"ldx.h",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38080000, 0xffff8000,	"ldx.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x380c0000, 0xffff8000,	"ldx.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38100000, 0xffff8000,	"stx.b",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38140000, 0xffff8000,	"stx.h",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38180000, 0xffff8000,	"stx.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x381c0000, 0xffff8000,	"stx.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38200000, 0xffff8000,	"ldx.bu",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38240000, 0xffff8000,	"ldx.hu",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38280000, 0xffff8000,	"ldx.wu",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x382c0000, 0xffff8000,	"preldx",	"u0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38300000, 0xffff8000,	"fldx.s",	"f0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38340000, 0xffff8000,	"fldx.d",	"f0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38380000, 0xffff8000,	"fstx.s",	"f0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x383c0000, 0xffff8000,	"fstx.d",	"f0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amswap.w",	"r,r,r,u0:0",			"amswap.w %1,%2,%3",	0,	0,	0 },
+  { 0x38600000, 0xffff8000,	"amswap.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amswap.d",	"r,r,r,u0:0",			"amswap.d %1,%2,%3",	0,	0,	0 },
+  { 0x38608000, 0xffff8000,	"amswap.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amadd.w",	"r,r,r,u0:0",			"amadd.w %1,%2,%3",	0,	0,	0 },
+  { 0x38610000, 0xffff8000,	"amadd.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amadd.d",	"r,r,r,u0:0",			"amadd.d %1,%2,%3",	0,	0,	0 },
+  { 0x38618000, 0xffff8000,	"amadd.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amand.w",	"r,r,r,u0:0",			"amand.w %1,%2,%3",	0,	0,	0 },
+  { 0x38620000, 0xffff8000,	"amand.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amand.d",	"r,r,r,u0:0",			"amand.d %1,%2,%3",	0,	0,	0 },
+  { 0x38628000, 0xffff8000,	"amand.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amor.w",	"r,r,r,u0:0",			"amor.w %1,%2,%3",	0,	0,	0 },
+  { 0x38630000, 0xffff8000,	"amor.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amor.d",	"r,r,r,u0:0",			"amor.d %1,%2,%3",	0,	0,	0 },
+  { 0x38638000, 0xffff8000,	"amor.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amxor.w",	"r,r,r,u0:0",			"amxor.w %1,%2,%3",	0,	0,	0 },
+  { 0x38640000, 0xffff8000,	"amxor.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amxor.d",	"r,r,r,u0:0",			"amxor.d %1,%2,%3",	0,	0,	0 },
+  { 0x38648000, 0xffff8000,	"amxor.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammax.w",	"r,r,r,u0:0",			"ammax.w %1,%2,%3",	0,	0,	0 },
+  { 0x38650000, 0xffff8000,	"ammax.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammax.d",	"r,r,r,u0:0",			"ammax.d %1,%2,%3",	0,	0,	0 },
+  { 0x38658000, 0xffff8000,	"ammax.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammin.w",	"r,r,r,u0:0",			"ammin.w %1,%2,%3",	0,	0,	0 },
+  { 0x38660000, 0xffff8000,	"ammin.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammin.d",	"r,r,r,u0:0",			"ammin.d %1,%2,%3",	0,	0,	0 },
+  { 0x38668000, 0xffff8000,	"ammin.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammax.wu",	"r,r,r,u0:0",			"ammax.wu %1,%2,%3",	0,	0,	0 },
+  { 0x38670000, 0xffff8000,	"ammax.wu",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammax.du",	"r,r,r,u0:0",			"ammax.du %1,%2,%3",	0,	0,	0 },
+  { 0x38678000, 0xffff8000,	"ammax.du",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammin.wu",	"r,r,r,u0:0",			"ammin.wu %1,%2,%3",	0,	0,	0 },
+  { 0x38680000, 0xffff8000,	"ammin.wu",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammin.du",	"r,r,r,u0:0",			"ammin.du %1,%2,%3",	0,	0,	0 },
+  { 0x38688000, 0xffff8000,	"ammin.du",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amswap_db.w",	"r,r,r,u0:0",			"amswap_db.w %1,%2,%3",	0,	0,	0 },
+  { 0x38690000, 0xffff8000,	"amswap_db.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amswap_db.d",	"r,r,r,u0:0",			"amswap_db.d %1,%2,%3",	0,	0,	0 },
+  { 0x38698000, 0xffff8000,	"amswap_db.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amadd_db.w",	"r,r,r,u0:0",			"amadd_db.w %1,%2,%3",	0,	0,	0 },
+  { 0x386a0000, 0xffff8000,	"amadd_db.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amadd_db.d",	"r,r,r,u0:0",			"amadd_db.d %1,%2,%3",	0,	0,	0 },
+  { 0x386a8000, 0xffff8000,	"amadd_db.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amand_db.w",	"r,r,r,u0:0",			"amand_db.w %1,%2,%3",	0,	0,	0 },
+  { 0x386b0000, 0xffff8000,	"amand_db.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amand_db.d",	"r,r,r,u0:0",			"amand_db.d %1,%2,%3",	0,	0,	0 },
+  { 0x386b8000, 0xffff8000,	"amand_db.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amor_db.w",	"r,r,r,u0:0",			"amor_db.w %1,%2,%3",	0,	0,	0 },
+  { 0x386c0000, 0xffff8000,	"amor_db.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amor_db.d",	"r,r,r,u0:0",			"amor_db.d %1,%2,%3",	0,	0,	0 },
+  { 0x386c8000, 0xffff8000,	"amor_db.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amxor_db.w",	"r,r,r,u0:0",			"amxor_db.w %1,%2,%3",	0,	0,	0 },
+  { 0x386d0000, 0xffff8000,	"amxor_db.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"amxor_db.d",	"r,r,r,u0:0",			"amxor_db.d %1,%2,%3",	0,	0,	0 },
+  { 0x386d8000, 0xffff8000,	"amxor_db.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammax_db.w",	"r,r,r,u0:0",			"ammax_db.w %1,%2,%3",	0,	0,	0 },
+  { 0x386e0000, 0xffff8000,	"ammax_db.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammax_db.d",	"r,r,r,u0:0",			"ammax_db.d %1,%2,%3",	0,	0,	0 },
+  { 0x386e8000, 0xffff8000,	"ammax_db.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammin_db.w",	"r,r,r,u0:0",			"ammin_db.w %1,%2,%3",	0,	0,	0 },
+  { 0x386f0000, 0xffff8000,	"ammin_db.w",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammin_db.d",	"r,r,r,u0:0",			"ammin_db.d %1,%2,%3",	0,	0,	0 },
+  { 0x386f8000, 0xffff8000,	"ammin_db.d",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammax_db.wu",	"r,r,r,u0:0",			"ammax_db.wu %1,%2,%3",	0,	0,	0 },
+  { 0x38700000, 0xffff8000,	"ammax_db.wu",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammax_db.du",	"r,r,r,u0:0",			"ammax_db.du %1,%2,%3",	0,	0,	0 },
+  { 0x38708000, 0xffff8000,	"ammax_db.du",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammin_db.wu",	"r,r,r,u0:0",			"ammin_db.wu %1,%2,%3",	0,	0,	0 },
+  { 0x38710000, 0xffff8000,	"ammin_db.wu",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x0,	0x0,		"ammin_db.du",	"r,r,r,u0:0",			"ammin_db.du %1,%2,%3",	0,	0,	0 },
+  { 0x38718000, 0xffff8000,	"ammin_db.du",	"r0:5,r10:5,r5:5",		0,			0,	0,	0 },
+  { 0x38720000, 0xffff8000,	"dbar",		"u0:15",			0,			0,	0,	0 },
+  { 0x38728000, 0xffff8000,	"ibar",		"u0:15",			0,			0,	0,	0 },
+  { 0x38740000, 0xffff8000,	"fldgt.s",	"f0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38748000, 0xffff8000,	"fldgt.d",	"f0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38750000, 0xffff8000,	"fldle.s",	"f0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38758000, 0xffff8000,	"fldle.d",	"f0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38760000, 0xffff8000,	"fstgt.s",	"f0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38768000, 0xffff8000,	"fstgt.d",	"f0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38770000, 0xffff8000,	"fstle.s",	"f0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38778000, 0xffff8000,	"fstle.d",	"f0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38780000, 0xffff8000,	"ldgt.b",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38788000, 0xffff8000,	"ldgt.h",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38790000, 0xffff8000,	"ldgt.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x38798000, 0xffff8000,	"ldgt.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x387a0000, 0xffff8000,	"ldle.b",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x387a8000, 0xffff8000,	"ldle.h",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x387b0000, 0xffff8000,	"ldle.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x387b8000, 0xffff8000,	"ldle.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x387c0000, 0xffff8000,	"stgt.b",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x387c8000, 0xffff8000,	"stgt.h",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x387d0000, 0xffff8000,	"stgt.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x387d8000, 0xffff8000,	"stgt.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x387e0000, 0xffff8000,	"stle.b",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x387e8000, 0xffff8000,	"stle.h",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x387f0000, 0xffff8000,	"stle.w",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0x387f8000, 0xffff8000,	"stle.d",	"r0:5,r5:5,r10:5",		0,			0,	0,	0 },
+  { 0 } /* Terminate the list.  */
+};
+
+static struct loongarch_opcode loongarch_jmp_opcodes[] =
+{
+  /* match,	mask,		name,		format,				macro,			include, exclude, pinfo.  */
+  { 0x0,	0x0,		"bltz",		"r,la",				"bltz %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x60000000, 0xfc00001f,	"bltz",		"r5:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bgtz",		"r,la",				"bgtz %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x60000000, 0xfc0003e0,	"bgtz",		"r0:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bgez",		"r,la",				"bgez %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x64000000, 0xfc00001f,	"bgez",		"r5:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"blez",		"r,la",				"blez %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x64000000, 0xfc0003e0,	"blez",		"r0:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"beqz",		"r,la",				"beqz %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x40000000, 0xfc000000,	"beqz",		"r5:5,sb0:5|10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bnez",		"r,la",				"bnez %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x44000000, 0xfc000000,	"bnez",		"r5:5,sb0:5|10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bceqz",	"c,la",				"bceqz %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x48000000, 0xfc000300,	"bceqz",	"c5:3,sb0:5|10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bcnez",	"c,la",				"bcnez %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x48000100, 0xfc000300,	"bcnez",	"c5:3,sb0:5|10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"jr",		"r",				"jirl $r0,%1,0",		0, 0, 0 },
+  { 0x50000000, 0xfc000000,	"b",		"sb0:10|10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"b",		"la",				"b %%pcrel(%1)",		0, 0, 0 },
+  { 0x4c000000, 0xfc000000,	"jirl",		"r0:5,r5:5,s10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bl",		"la",				"bl %%pcrel(%1)",		0, 0, 0 },
+  { 0x54000000, 0xfc000000,	"bl",		"sb0:10|10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"beq",		"r,r,la",			"beq %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x58000000, 0xfc000000,	"beq",		"r5:5,r0:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bne",		"r,r,la",			"bne %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x5c000000, 0xfc000000,	"bne",		"r5:5,r0:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"blt",		"r,r,la",			"blt %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x60000000, 0xfc000000,	"blt",		"r5:5,r0:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bgt",		"r,r,la",			"bgt %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x60000000, 0xfc000000,	"bgt",		"r0:5,r5:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bge",		"r,r,la",			"bge %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x64000000, 0xfc000000,	"bge",		"r5:5,r0:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"ble",		"r,r,la",			"ble %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x64000000, 0xfc000000,	"ble",		"r0:5,r5:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bltu",		"r,r,la",			"bltu %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x68000000, 0xfc000000,	"bltu",		"r5:5,r0:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bgtu",		"r,r,la",			"bgtu %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x68000000, 0xfc000000,	"bgtu",		"r0:5,r5:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bgeu",		"r,r,la",			"bgeu %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x6c000000, 0xfc000000,	"bgeu",		"r5:5,r0:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0x0,	0x0,		"bleu",		"r,r,la",			"bleu %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x6c000000, 0xfc000000,	"bleu",		"r0:5,r5:5,sb10:16<<2",		0,				0, 0, 0 },
+  { 0 } /* Terminate the list.  */
+};
+
+struct loongarch_ase loongarch_ASEs[] =
+{
+  { &LARCH_opts.ase_fix, loongarch_macro_opcodes,	0, 0, { 0 }, 0, 0 },
+  { &LARCH_opts.ase_fix, loongarch_lmm_opcodes,		0, 0, { 0 }, 0, 0 },
+  { &LARCH_opts.ase_fix, loongarch_privilege_opcodes,	0, 0, { 0 }, 0, 0 },
+  { &LARCH_opts.ase_fix, loongarch_jmp_opcodes,		0, 0, { 0 }, 0, 0 },
+  { &LARCH_opts.ase_fix, loongarch_load_store_opcodes,	0, 0, { 0 }, 0, 0 },
+  { &LARCH_opts.ase_fix, loongarch_fix_opcodes,		0, 0, { 0 }, 0, 0 },
+  { &LARCH_opts.ase_float, loongarch_4opt_opcodes,	0, 0, { 0 }, 0, 0 },
+  { &LARCH_opts.ase_float, loongarch_float_opcodes,	0, 0, { 0 }, 0, 0 },
+
+  { 0 },
+};
-- 
2.27.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 7/7][LoongArch] Opcodes support
  2021-08-14  8:20 [PATCH 7/7][LoongArch] Opcodes support Paul Hua
@ 2021-08-14 17:58 ` Fangrui Song
  2021-08-24 19:52   ` Fangrui Song
  2021-08-21 12:45 ` WANG Xuerui
  2021-08-24 11:49 ` Alan Modra
  2 siblings, 1 reply; 5+ messages in thread
From: Fangrui Song @ 2021-08-14 17:58 UTC (permalink / raw)
  To: Paul Hua
  Cc: binutils, Xu Chenghua, caiyinyu, chenglulu, huangpei, liuzhensong

> +static struct loongarch_opcode loongarch_macro_opcodes[] =
> +{
> [...]
> +  { 0, 0, "la.pcrel", "r,la",
> +    "pcaddu12i %1,%%pcrel(%2+0x800)>>12;"
> +    "addi.d %1,%1,%%pcrel(%2+4)-(%%pcrel(%2+4+0x800)>>12<<12);",
> +    &LARCH_opts.addrwidth_is_64, 0, 0 },
> +  { 0, 0, "la.pcrel", "r,r,la",
> +    "pcaddu12i %1,(%%pcrel(%3)-(%%pcrel(%3+0x80000000)>>32<<32))<<32>>44;"
> +    "ori %2,$r0,(%%pcrel(%3+4)-(%%pcrel(%3+4+0x80000000)>>32<<32))&0xfff;"
> +    "lu32i.d %2,%%pcrel(%3+8+0x80000000)<<12>>44;"
> +    "lu52i.d %2,%2,%%pcrel(%3+12+0x80000000)>>52;"
> +    "add.d %1,%1,%2;",
> +    &LARCH_opts.addrwidth_is_64, 0, 0 },
> +
> +  { 0, 0, "la.got", "r,l",
> +    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%gprel(%2))<<32>>44;"
> +    "ld.w "
> +    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%gprel(%2)-((%%pcrel(_GLOBAL_"
> +    "OFFSET_TABLE_+4+0x800)+%%gprel(%2))>>12<<12);",
> +    &LARCH_opts.addrwidth_is_32, 0, 0 },

Relocations don't need support for arbitrary shifts.  Just hard code the
possible shift variants and the implementation will be much cleaner, no
need for a long list of R_LARCH_SOP_PUSH_*.

See xen0n's comment on https://github.com/loongson/LoongArch-Documentation/pull/3#issuecomment-894672071

> Plus it seems there's a stack machine implemented with relocation records. I think this is bad for these reasons:
> 
> Fragility could ensue due to tools not respecting relative order of relocation records on the same address;
> It's superficial, conveying actions and not motivations, which is generally bad smell in software engineering;
> Not much complexity is shed by the introduction of intended flexibility here, so it wasn't exactly profitable either;
> No prior cases wheresoever found in public code, so it's not sure if the scheme is to be appreciated universally.

Tests nearly do not exist. There is only a nop test.

% ls gas/testsuite/gas/loongarch/
nop.d  loongarch.exp  nop.s

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 7/7][LoongArch] Opcodes support
  2021-08-14  8:20 [PATCH 7/7][LoongArch] Opcodes support Paul Hua
  2021-08-14 17:58 ` Fangrui Song
@ 2021-08-21 12:45 ` WANG Xuerui
  2021-08-24 11:49 ` Alan Modra
  2 siblings, 0 replies; 5+ messages in thread
From: WANG Xuerui @ 2021-08-21 12:45 UTC (permalink / raw)
  To: binutils, paul.hua.gm

Hi Paul,

I just took a quick look at the code and have several 
questions/suggestions off the top of my head:

- There is support for the undocumented vector extensions. Maybe don't 
include them until documentation is released? Otherwise we'll be 
shipping dead code. BTW the "$xNN" names for the LASX registers sound 
horrible, they're bound to confuse people with AArch64/RISC-V backgrounds.

- ABI names for integer registers are not updated to match the latest 
LoongArch ELF psABI doc[1]: "$x" still remains, "$s9" is not recognized.

- Also, I don't think "v0/v1" aliases for a0/a1 are necessary any more; 
in an earlier presentation (maybe in 2020?) it is rumored that LoongArch 
assembler is able to consume MIPS assembly, but apparently that 
component is not present here. So we don't need to pretend the separate 
MIPS-like dedicated return value registers exist; other arches don't do 
that, either. I suggest just removing these and be done with that.

- The opcode table seems to be hand-written, which can get fragile and 
hard to maintain. Grouping of instructions seems rather arbitrary. 
There's a machine-readable list of documented LoongArch instructions[2] 
(compiled and edited by me); it would be better to derive the opcode 
table from there.

- The "alsl.X" instructions are implemented differently wrt the manual. 
According to the manual[3], hardware executes "sa2+1" while the syntax 
says just "sa2"; that means, for example, "alsl.d rd, rj, rk, 3" should 
execute the operation "rd = rk + rj << 4". However the instruction 
format in the patch seems to suggest the syntax "alsl.d rd, rj, rk, 4" 
which is clearly not the same.

- Many of the branch instructions are redundant, and can be implemented 
as macros. I already made a patch[4] to demonstrate the right way to do 
these.


[1]: 
https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html

[2]: https://github.com/loongson-community/loongarch-opcodes

[3]: 
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#_alsl_wud

[4]: https://github.com/loongson/binutils-gdb/pull/38


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 7/7][LoongArch] Opcodes support
  2021-08-14  8:20 [PATCH 7/7][LoongArch] Opcodes support Paul Hua
  2021-08-14 17:58 ` Fangrui Song
  2021-08-21 12:45 ` WANG Xuerui
@ 2021-08-24 11:49 ` Alan Modra
  2 siblings, 0 replies; 5+ messages in thread
From: Alan Modra @ 2021-08-24 11:49 UTC (permalink / raw)
  To: Paul Hua
  Cc: binutils, Xu Chenghua, caiyinyu, chenglulu, huangpei, liuzhensong

This looks reasonable to me, with the exception of UB on left shift of
negative signed values.  Please compile with
 -fsanitize=address,undefined
then exercise the code to catch errors.

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 7/7][LoongArch] Opcodes support
  2021-08-14 17:58 ` Fangrui Song
@ 2021-08-24 19:52   ` Fangrui Song
  0 siblings, 0 replies; 5+ messages in thread
From: Fangrui Song @ 2021-08-24 19:52 UTC (permalink / raw)
  To: Paul Hua
  Cc: binutils, Xu Chenghua, caiyinyu, chenglulu, huangpei,
	liuzhensong, WANG Xuerui, Alan Modra

I filed https://github.com/loongson/LoongArch-Documentation/issues/9
so that the stack machine relocation concerns won't be buried in dust.

On 2021-08-14, Fangrui Song wrote:
>>+static struct loongarch_opcode loongarch_macro_opcodes[] =
>>+{
>>[...]
>>+  { 0, 0, "la.pcrel", "r,la",
>>+    "pcaddu12i %1,%%pcrel(%2+0x800)>>12;"
>>+    "addi.d %1,%1,%%pcrel(%2+4)-(%%pcrel(%2+4+0x800)>>12<<12);",
>>+    &LARCH_opts.addrwidth_is_64, 0, 0 },
>>+  { 0, 0, "la.pcrel", "r,r,la",
>>+    "pcaddu12i %1,(%%pcrel(%3)-(%%pcrel(%3+0x80000000)>>32<<32))<<32>>44;"
>>+    "ori %2,$r0,(%%pcrel(%3+4)-(%%pcrel(%3+4+0x80000000)>>32<<32))&0xfff;"
>>+    "lu32i.d %2,%%pcrel(%3+8+0x80000000)<<12>>44;"
>>+    "lu52i.d %2,%2,%%pcrel(%3+12+0x80000000)>>52;"
>>+    "add.d %1,%1,%2;",
>>+    &LARCH_opts.addrwidth_is_64, 0, 0 },
>>+
>>+  { 0, 0, "la.got", "r,l",
>>+    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%gprel(%2))<<32>>44;"
>>+    "ld.w "
>>+    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%gprel(%2)-((%%pcrel(_GLOBAL_"
>>+    "OFFSET_TABLE_+4+0x800)+%%gprel(%2))>>12<<12);",
>>+    &LARCH_opts.addrwidth_is_32, 0, 0 },
>
>Relocations don't need support for arbitrary shifts.  Just hard code the
>possible shift variants and the implementation will be much cleaner, no
>need for a long list of R_LARCH_SOP_PUSH_*.
>
>See xen0n's comment on https://github.com/loongson/LoongArch-Documentation/pull/3#issuecomment-894672071
>
>>Plus it seems there's a stack machine implemented with relocation records. I think this is bad for these reasons:
>>
>>Fragility could ensue due to tools not respecting relative order of relocation records on the same address;
>>It's superficial, conveying actions and not motivations, which is generally bad smell in software engineering;
>>Not much complexity is shed by the introduction of intended flexibility here, so it wasn't exactly profitable either;
>>No prior cases wheresoever found in public code, so it's not sure if the scheme is to be appreciated universally.
>
>Tests nearly do not exist. There is only a nop test.
>
>% ls gas/testsuite/gas/loongarch/
>nop.d  loongarch.exp  nop.s

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2021-08-24 19:52 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-14  8:20 [PATCH 7/7][LoongArch] Opcodes support Paul Hua
2021-08-14 17:58 ` Fangrui Song
2021-08-24 19:52   ` Fangrui Song
2021-08-21 12:45 ` WANG Xuerui
2021-08-24 11:49 ` Alan Modra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).