[PATCH 3/3] RISC-V: Add support for XAndesPerf extension

Ethan Y. C. Liang ycl669@andestech.com
Wed Nov 26 08:47:55 GMT 2025


The XAndesPerf extension provides performance enhancement instructions
for Andes RISC-V processors, including branch instructions, load
effective address instructions, GP-relative instructions, and string
processing instructions.

Spec: https://github.com/andestech/andes-v5-isa/releases/download/ast-v5_4_0-release/AndeStar_V5_ISA_Spec_UM165-v1.5.08-20250317.pdf

Contributors:
  Ethan Y. C. Liang <ycl669@andestech.com>
  Yu-Qi Liu <yuqiliu@andestech.com>

bfd/ChangeLog

	* bfd-in2.h: Add XAndesPerf relocation types.
	* libbfd.h: Likewise.
	* reloc.c: Likewise.
	* elfnn-riscv.c: Add support for XAndesPerf relocations and GP
	  relaxation. (riscv_has_subset is adapted from
	  riscv_use_table_jump in Jiawei's patch [1].)
	* elfxx-riscv.c: Add XAndesPerf extension, instruction class,
	  relocation howto entries, and relocation mapping entries.
	* elfxx-riscv.h: Add enum for relaxation passes.

gas/ChangeLog

	* config/tc-riscv.c: Add support for XAndesPerf instruction
	  assembly and branch relaxation.
	* doc/c-riscv.texi: Document XAndesPerf extension.
	* testsuite/gas/riscv/march-help.l: Add xandesperf to available
	  -march extensions.
	* testsuite/gas/riscv/x-andes-perf.d: New test.
	* testsuite/gas/riscv/x-andes-perf.s: Likewise.

include/ChangeLog

	* elf/riscv.h: Add vendor enum, vendor id, and vendor string for
	  Andes relocation.
	* opcode/riscv-opc.h: Add opcode macros for XAndesPerf
	  instructions.
	* opcode/riscv.h: Add immediate macros for XAndesPerf
	  instructions.

ld/ChangeLog

	* emultempl/riscvelf.em: Change link_info.relax_pass from 2 to 4.
	* testsuite/ld-riscv-elf/ld-riscv-elf.exp: Add XAndesPerf tests to
	  runtest list.
	* testsuite/ld-riscv-elf/x-andes-perf-relax.d: New test.
	* testsuite/ld-riscv-elf/x-andes-perf-relax.ld: Likewise.
	* testsuite/ld-riscv-elf/x-andes-perf-relax.s: Likewise.
	* testsuite/ld-riscv-elf/x-andes-perf-reloc.d: New test.
	* testsuite/ld-riscv-elf/x-andes-perf-reloc.s: Likewise.

opcodes/ChangeLog

	* riscv-dis.c Add support for XAndesPerf instruction disassembly.
	* riscv-opc.c: Add XAndesPerf instruction entries.

[1] https://sourceware.org/pipermail/binutils/2024-January/131573.html
---
 bfd/bfd-in2.h                                 |  11 +
 bfd/elfnn-riscv.c                             | 693 +++++++++++++++++-
 bfd/elfxx-riscv.c                             | 183 +++++
 bfd/elfxx-riscv.h                             |   8 +
 bfd/libbfd.h                                  |   9 +
 bfd/reloc.c                                   |  21 +
 gas/config/tc-riscv.c                         | 307 +++++++-
 gas/doc/c-riscv.texi                          |   5 +
 gas/testsuite/gas/riscv/march-help.l          |   1 +
 gas/testsuite/gas/riscv/x-andes-perf.d        | 107 +++
 gas/testsuite/gas/riscv/x-andes-perf.s        |  50 ++
 include/elf/riscv.h                           |  18 +
 include/opcode/riscv-opc.h                    |  89 +++
 include/opcode/riscv.h                        |  78 ++
 ld/emultempl/riscvelf.em                      |   2 +-
 ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp    |   3 +
 .../ld-riscv-elf/x-andes-perf-relax.d         |  14 +
 .../ld-riscv-elf/x-andes-perf-relax.ld        |  11 +
 .../ld-riscv-elf/x-andes-perf-relax.s         |  23 +
 .../ld-riscv-elf/x-andes-perf-reloc.d         |  26 +
 .../ld-riscv-elf/x-andes-perf-reloc.s         |  20 +
 opcodes/riscv-dis.c                           |  89 +++
 opcodes/riscv-opc.c                           |  31 +
 23 files changed, 1787 insertions(+), 12 deletions(-)
 create mode 100644 gas/testsuite/gas/riscv/x-andes-perf.d
 create mode 100644 gas/testsuite/gas/riscv/x-andes-perf.s
 create mode 100644 ld/testsuite/ld-riscv-elf/x-andes-perf-relax.d
 create mode 100644 ld/testsuite/ld-riscv-elf/x-andes-perf-relax.ld
 create mode 100644 ld/testsuite/ld-riscv-elf/x-andes-perf-relax.s
 create mode 100644 ld/testsuite/ld-riscv-elf/x-andes-perf-reloc.d
 create mode 100644 ld/testsuite/ld-riscv-elf/x-andes-perf-reloc.s

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 06e78a30a2b..07eecec43b2 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -5451,6 +5451,17 @@ enum bfd_reloc_code_real
   BFD_RELOC_RISCV_SUB_ULEB128,
   BFD_RELOC_RISCV_VENDOR,
 
+  /* RISC-V vendor (Andes) relocations.  */
+  BFD_RELOC_RISCV_NDS_BRANCH_10,
+  BFD_RELOC_RISCV_NDS_GPREL_18S0_I,
+  BFD_RELOC_RISCV_NDS_GPREL_17S1_I,
+  BFD_RELOC_RISCV_NDS_GPREL_17S2_I,
+  BFD_RELOC_RISCV_NDS_GPREL_17S3_I,
+  BFD_RELOC_RISCV_NDS_GPREL_18S0_S,
+  BFD_RELOC_RISCV_NDS_GPREL_17S1_S,
+  BFD_RELOC_RISCV_NDS_GPREL_17S2_S,
+  BFD_RELOC_RISCV_NDS_GPREL_17S3_S,
+
   /* Renesas RL78 Relocations.  */
   BFD_RELOC_RL78_NEG8,
   BFD_RELOC_RL78_NEG16,
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index f9e2f3a7597..2750578f11e 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -2014,6 +2014,64 @@ perform_relocation (const reloc_howto_type *howto,
       bfd_reloc_status_type status = bfd_reloc_ok;
       switch (vendor_id)
 	{
+	case RISCV_VENDOR_ID_NDS:
+	  switch (ELFNN_R_TYPE (rel->r_info))
+	    {
+	    case R_RISCV_NDS_BRANCH_10:
+	      if (!VALID_NDS_BRANCH_IMM10 (value))
+		return bfd_reloc_overflow;
+	      value = ENCODE_NDS_BRANCH_IMM10 (value);
+	      break;
+
+	    case R_RISCV_NDS_GPREL_18S0_I:
+	      if (!VALID_NDS_LBGP_IMM (value))
+		return bfd_reloc_overflow;
+	      value = ENCODE_NDS_LBGP_IMM (value);
+	      break;
+
+	    case R_RISCV_NDS_GPREL_17S1_I:
+	      if (!VALID_NDS_LHGP_IMM (value))
+		return bfd_reloc_overflow;
+	      value = ENCODE_NDS_LHGP_IMM (value);
+	      break;
+
+	    case R_RISCV_NDS_GPREL_17S2_I:
+	      if (!VALID_NDS_LWGP_IMM (value))
+		return bfd_reloc_overflow;
+	      value = ENCODE_NDS_LWGP_IMM (value);
+	      break;
+
+	    case R_RISCV_NDS_GPREL_17S3_I:
+	      if (!VALID_NDS_LDGP_IMM (value))
+		return bfd_reloc_overflow;
+	      value = ENCODE_NDS_LDGP_IMM (value);
+	      break;
+
+	    case R_RISCV_NDS_GPREL_18S0_S:
+	      if (!VALID_NDS_SBGP_IMM (value))
+		return bfd_reloc_overflow;
+	      value = ENCODE_NDS_SBGP_IMM (value);
+	      break;
+
+	    case R_RISCV_NDS_GPREL_17S1_S:
+	      if (!VALID_NDS_SHGP_IMM (value))
+		return bfd_reloc_overflow;
+	      value = ENCODE_NDS_SHGP_IMM (value);
+	      break;
+
+	    case R_RISCV_NDS_GPREL_17S2_S:
+	      if (!VALID_NDS_SWGP_IMM (value))
+		return bfd_reloc_overflow;
+	      value = ENCODE_NDS_SWGP_IMM (value);
+	      break;
+
+	    case R_RISCV_NDS_GPREL_17S3_S:
+	      if (!VALID_NDS_SDGP_IMM (value))
+		return bfd_reloc_overflow;
+	      value = ENCODE_NDS_SDGP_IMM (value);
+	      break;
+	    }
+	  break;
 	default:
 	  return bfd_reloc_notsupported;
 	}
@@ -2837,6 +2895,29 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	 type was R_RISCV_VENDOR.  */
       switch (vendor_id)
 	{
+	case RISCV_VENDOR_ID_NDS:
+	  switch (r_type)
+	    {
+	    case R_RISCV_NDS_GPREL_18S0_I:
+	    case R_RISCV_NDS_GPREL_17S1_I:
+	    case R_RISCV_NDS_GPREL_17S2_I:
+	    case R_RISCV_NDS_GPREL_17S3_I:
+	    case R_RISCV_NDS_GPREL_18S0_S:
+	    case R_RISCV_NDS_GPREL_17S1_S:
+	    case R_RISCV_NDS_GPREL_17S2_S:
+	    case R_RISCV_NDS_GPREL_17S3_S:
+	      {
+		bfd_vma gp = riscv_global_pointer_value (info);
+		rel->r_addend -= gp;
+		break;
+	      }
+	    case R_RISCV_NDS_BRANCH_10:
+	      break;
+	    default:
+	      /* Unreachable: vendor_id was was validated earlier.  */
+	      BFD_ASSERT (false);
+	    }
+	  goto do_relocation;
 	default:
 	  break;
 	}
@@ -4069,6 +4150,38 @@ riscv_i_or_e_p (bfd *ibfd,
   return true;
 }
 
+/* Return true if the output BFD contains this subset.  */
+
+static bool
+riscv_has_subset (struct bfd_link_info *info, const char *subset)
+{
+  unsigned xlen = ARCH_SIZE;
+  riscv_subset_list_t subsets;
+  bool ret;
+
+  if (info->disable_target_specific_optimizations >= 1)
+    return false;
+
+  if (!bfd_link_executable (info))
+    return false;
+
+  bfd *obfd = info->output_bfd;
+  obj_attribute *out_attr = elf_known_obj_attributes_proc (obfd);
+
+  memset (&subsets, 0, sizeof (riscv_subset_list_t));
+
+  riscv_parse_subset_t riscv_rps_ld_out =
+    {&subsets, _bfd_error_handler, &xlen, NULL, false};
+
+  if (!riscv_parse_subset (&riscv_rps_ld_out, out_attr[Tag_RISCV_arch].s))
+    return false;
+
+  ret = riscv_subset_supports (&riscv_rps_ld_out, subset);
+  riscv_release_subset_list (&subsets);
+
+  return ret;
+}
+
 /* Merge standard extensions.
 
    Return Value:
@@ -4570,6 +4683,7 @@ struct riscv_pcgp_hi_reloc
   asection *sym_sec;
   bool undefined_weak;
   riscv_pcgp_hi_reloc *next;
+  Elf_Internal_Rela *rel;
 };
 
 typedef struct riscv_pcgp_lo_reloc riscv_pcgp_lo_reloc;
@@ -5442,11 +5556,483 @@ bfd_elfNN_riscv_set_data_segment_info (struct bfd_link_info *info,
   htab->data_segment_phase = data_segment_phase;
 }
 
+/* Traverse all output sections and return the max alignment.
+   Only the output sections which are in the [gp-512K, gp+512K)
+   range need to be considered.  */
+static bfd_vma
+_bfd_riscv_nds_get_max_alignment (asection *sec, bfd_vma gp)
+{
+  unsigned int max_alignment_power = 0;
+  asection *o;
+  bfd_signed_vma offset_min = -(1 << 19);
+  bfd_signed_vma offset_max = (1 << 19) - 1;
+
+  for (o = sec->output_section->owner->sections; o != NULL; o = o->next)
+    {
+      bool valid = true;
+      bfd_signed_vma sec_begin_offset = sec_addr (o) - gp;
+      bfd_signed_vma sec_end_offset = sec_addr (o) + o->size - gp;
+      if (!((sec_begin_offset >= offset_min
+	     && sec_begin_offset <= offset_max)
+	    || (sec_end_offset >= offset_min
+		&& sec_end_offset <= offset_max)))
+	valid = false;
+
+      if (valid && o->alignment_power > max_alignment_power)
+	max_alignment_power = o->alignment_power;
+    }
+
+  return (bfd_vma) 1 << max_alignment_power;
+}
+
+/* Check alignment of symbol and addend.  */
+static int
+riscv_nds_min_p2align (Elf_Internal_Rela *rel, asection *sec, bfd_vma symval)
+{
+  int limit = 3 < sec->alignment_power ? 3 : sec->alignment_power;
+  bfd_vma sym_offset = symval - sec->output_section->vma - sec->output_offset;
+  int i;
+  for (i = 0; i < limit; ++i)
+    {
+      unsigned int m = 1u << i;
+      if ((rel && (rel->r_addend & m)) || sym_offset & m)
+	break;
+    }
+
+  return i;
+}
+
+/* Check instruction type and distance of symbol.  */
+static int
+riscv_nds_relax_gp_insn (bfd *abfd, asection *sec, asection *sym_sec,
+			 Elf_Internal_Rela *rel, bfd_vma symval, unsigned sym,
+			 bfd_vma addend, bfd_vma gp, bfd_vma reserve_size)
+{
+  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+  uint32_t insn = bfd_get_32 (abfd, contents + rel->r_offset);
+  bfd_signed_vma bias = symval - gp;
+  bias += (bias > 0) ? reserve_size : -reserve_size;
+  int gp_alignment = ffs (gp) - 1;
+
+  int worst_p2align = riscv_nds_min_p2align (rel, sym_sec, symval);
+
+  if ((insn & MASK_ADDI) == MATCH_ADDI && VALID_NDS_LBGP_IMM (bias))
+    {
+      rel->r_info = ELFNN_R_INFO (sym, R_RISCV_NDS_GPREL_18S0_I);
+      insn = (insn & (OP_MASK_RD << OP_SH_RD)) | MATCH_NDS_ADDIGP;
+    }
+  else if ((insn & MASK_LB) == MATCH_LB && VALID_NDS_LBGP_IMM (bias))
+    {
+      rel->r_info = ELFNN_R_INFO (sym, R_RISCV_NDS_GPREL_18S0_I);
+      insn = (insn & (OP_MASK_RD << OP_SH_RD)) | MATCH_NDS_LBGP;
+    }
+  else if ((insn & MASK_LBU) == MATCH_LBU && VALID_NDS_LBGP_IMM (bias))
+    {
+      rel->r_info = ELFNN_R_INFO (sym, R_RISCV_NDS_GPREL_18S0_I);
+      insn = (insn & (OP_MASK_RD << OP_SH_RD)) | MATCH_NDS_LBUGP;
+    }
+  else if ((insn & MASK_LH) == MATCH_LH && VALID_NDS_LBGP_IMM (bias)
+	   && worst_p2align > 0 && gp_alignment > 0)
+    {
+      rel->r_info = ELFNN_R_INFO (sym, R_RISCV_NDS_GPREL_17S1_I);
+      insn = (insn & (OP_MASK_RD << OP_SH_RD)) | MATCH_NDS_LHGP;
+    }
+  else if ((insn & MASK_LHU) == MATCH_LHU && VALID_NDS_LBGP_IMM (bias)
+	   && worst_p2align > 0 && gp_alignment > 0)
+    {
+      rel->r_info = ELFNN_R_INFO (sym, R_RISCV_NDS_GPREL_17S1_I);
+      insn = (insn & (OP_MASK_RD << OP_SH_RD)) | MATCH_NDS_LHUGP;
+    }
+  else if ((insn & MASK_LW) == MATCH_LW && bias >= -(1 << 18)
+	   && bias < (1 << 18) && worst_p2align > 1 && gp_alignment > 1)
+    {
+      rel->r_info = ELFNN_R_INFO (sym, R_RISCV_NDS_GPREL_17S2_I);
+      insn = (insn & (OP_MASK_RD << OP_SH_RD)) | MATCH_NDS_LWGP;
+    }
+  else if ((insn & MASK_LWU) == MATCH_LWU && bias >= -(1 << 18)
+	   && bias < (1 << 18) && worst_p2align > 1 && gp_alignment > 1)
+    {
+      rel->r_info = ELFNN_R_INFO (sym, R_RISCV_NDS_GPREL_17S2_I);
+      insn = (insn & (OP_MASK_RD << OP_SH_RD)) | MATCH_NDS_LWUGP;
+    }
+  else if ((insn & MASK_LD) == MATCH_LD && bias >= -(1 << 19)
+	   && bias < (1 << 19) && worst_p2align > 2 && gp_alignment > 2
+	   && ARCH_SIZE == 64)
+    {
+      rel->r_info = ELFNN_R_INFO (sym, R_RISCV_NDS_GPREL_17S3_I);
+      insn = (insn & (OP_MASK_RD << OP_SH_RD)) | MATCH_NDS_LDGP;
+    }
+  else if ((insn & MASK_SB) == MATCH_SB && VALID_NDS_SBGP_IMM (bias))
+    {
+      rel->r_info = ELFNN_R_INFO (sym, R_RISCV_NDS_GPREL_18S0_S);
+      insn = (insn & (OP_MASK_RS2 << OP_SH_RS2)) | MATCH_NDS_SBGP;
+    }
+  else if ((insn & MASK_SH) == MATCH_SH && VALID_NDS_SBGP_IMM (bias)
+	   && worst_p2align > 0 && gp_alignment > 0)
+    {
+      rel->r_info = ELFNN_R_INFO (sym, R_RISCV_NDS_GPREL_17S1_S);
+      insn = (insn & (OP_MASK_RS2 << OP_SH_RS2)) | MATCH_NDS_SHGP;
+    }
+  else if ((insn & MASK_SW) == MATCH_SW && bias >= -(1 << 18)
+	   && bias < (1 << 18) && worst_p2align > 1 && gp_alignment > 1)
+    {
+      rel->r_info = ELFNN_R_INFO (sym, R_RISCV_NDS_GPREL_17S2_S);
+      insn = (insn & (OP_MASK_RS2 << OP_SH_RS2)) | MATCH_NDS_SWGP;
+    }
+  else if ((insn & MASK_SD) == MATCH_SD && bias >= -(1 << 19)
+	   && bias < (1 << 19) && worst_p2align > 2 && gp_alignment > 2
+	   && ARCH_SIZE == 64)
+    {
+      rel->r_info = ELFNN_R_INFO (sym, R_RISCV_NDS_GPREL_17S3_S);
+      insn = (insn & (OP_MASK_RS2 << OP_SH_RS2)) | MATCH_NDS_SDGP;
+    }
+  else
+    {
+      return false;
+    }
+  rel->r_addend = addend;
+
+  bfd_put_32 (abfd, insn, contents + rel->r_offset);
+
+  return true;
+}
+
+/* Structure used for recording symbol info for GP relaxation in
+   _bfd_riscv_nds_relax_lui.  */
+typedef struct relax_gp_sym_info
+{
+  Elf_Internal_Sym *lsym;
+  struct elf_link_hash_entry *h;
+  asection *sec;
+  struct relax_gp_sym_info *next;
+} relax_gp_sym_info_t;
+static relax_gp_sym_info_t *relax_gp_sym_info_head = NULL;
+
+/* Record symbol if record is 1.
+   Find symbol if record is 0.  */
+static relax_gp_sym_info_t *
+riscv_nds_record_and_find_relax_gp_syms (asection *sec,
+					 Elf_Internal_Sym *lsym,
+					 struct elf_link_hash_entry *h,
+					 int record)
+{
+  struct relax_gp_sym_info *ptr, *pre_ptr;
+  ptr = relax_gp_sym_info_head;
+  pre_ptr = ptr;
+
+  /* Check whether the symbol is recorded.  */
+  while (ptr)
+    {
+      if ((h && h == ptr->h && sec == ptr->sec)
+	  || (lsym && lsym == ptr->lsym && sec == ptr->sec))
+	return ptr;
+      pre_ptr = ptr;
+      ptr = ptr->next;
+    }
+
+  if (!ptr && record)
+    {
+      ptr = bfd_malloc (sizeof (struct relax_gp_sym_info));
+      if (!ptr)
+	return NULL;
+      ptr->sec = sec;
+      ptr->lsym = lsym;
+      ptr->h = h;
+      ptr->next = NULL;
+
+      if (!relax_gp_sym_info_head)
+	relax_gp_sym_info_head = ptr;
+      else
+	pre_ptr->next = ptr;
+
+      return ptr;
+    }
+
+  return NULL;
+}
+
+/* PASS_XANDESPERF_GP1: Modify low part insns
+   PASS_XANDESPERF_GP2: Remove redundant high part insns.  */
+static bool
+_bfd_riscv_nds_relax_lui (bfd *abfd,
+			  asection *sec,
+			  asection *sym_sec,
+			  struct bfd_link_info *link_info,
+			  Elf_Internal_Rela *rel,
+			  bfd_vma symval,
+			  bfd_vma max_alignment,
+			  bfd_vma reserve_size,
+			  bool *again ATTRIBUTE_UNUSED,
+			  riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+			  bool undefined_weak)
+{
+  bfd_vma gp = riscv_global_pointer_value (link_info);
+  bfd_vma data_segment_alignment = link_info->relro
+				   ? ELF_MAXPAGESIZE + ELF_COMMONPAGESIZE
+				   : ELF_MAXPAGESIZE;
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+  Elf_Internal_Sym *isym = NULL;
+  struct elf_link_hash_entry *hsym = NULL;
+
+  BFD_ASSERT (rel->r_offset + 4 <= sec->size);
+
+  /* Skip some unsupported cases.  */
+  if (undefined_weak || !gp || sym_sec->output_section == bfd_abs_section_ptr
+      || elf_section_flags (sym_sec) & SHF_EXECINSTR)
+    return true;
+
+  /* If gp and the symbol are in the same output section, which is not the
+     abs section, then consider only that output section's alignment.  */
+  struct bfd_link_hash_entry *h =
+    bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, false, false,
+			  true);
+  if (h->u.def.section->output_section == sym_sec->output_section
+      && sym_sec->output_section != bfd_abs_section_ptr)
+    max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power;
+  else
+    {
+      /* Consider output section alignments which are in [gp-512K, gp+512K). */
+      max_alignment = _bfd_riscv_nds_get_max_alignment (sec, gp);
+    }
+
+  if (symval < sec_addr (sym_sec)
+      || symval > (sec_addr (sym_sec) + sym_sec->size))
+    max_alignment = data_segment_alignment > max_alignment
+		    ? data_segment_alignment : max_alignment;
+
+  /* Record the symbols referenced by non-relaxed low part instructions during
+     PASS_XANDESPERF_GP1, and do not delete the high part instructions that
+     reference these symbols during PASS_XANDESPERF_GP2.  */
+  if (symtab_hdr->sh_info != 0
+      && !symtab_hdr->contents
+      && !(symtab_hdr->contents =
+	   (unsigned char *) bfd_elf_get_elf_syms (abfd, symtab_hdr,
+						   symtab_hdr->sh_info,
+						   0, NULL, NULL, NULL)))
+    return false;
+
+  if (ELFNN_R_SYM (rel->r_info) < symtab_hdr->sh_info)
+    /* A local symbol.  */
+    isym = ((Elf_Internal_Sym *) symtab_hdr->contents
+	    + ELFNN_R_SYM (rel->r_info));
+  else
+    {
+      /* A global symbol.  */
+      unsigned long indx;
+      indx = ELFNN_R_SYM (rel->r_info) - symtab_hdr->sh_info;
+      hsym = elf_sym_hashes (abfd)[indx];
+
+      while (hsym->root.type == bfd_link_hash_indirect
+	     || hsym->root.type == bfd_link_hash_warning)
+	hsym = (struct elf_link_hash_entry *) hsym->root.u.i.link;
+    }
+
+  reserve_size += max_alignment;
+  switch (ELFNN_R_TYPE (rel->r_info))
+    {
+    case R_RISCV_LO12_I:
+    case R_RISCV_LO12_S:
+      {
+	/* Record high part that shouldn't be deleted.  */
+	if (!riscv_nds_relax_gp_insn (abfd, sec, sym_sec, rel,
+				      symval, ELFNN_R_SYM (rel->r_info),
+				      rel->r_addend, gp, reserve_size)
+	    && !riscv_nds_record_and_find_relax_gp_syms (sym_sec, isym,
+							 hsym, 1))
+	  return false;
+      }
+      break;
+
+    case R_RISCV_HI20:
+      if (!riscv_nds_record_and_find_relax_gp_syms (sym_sec, isym, hsym, 0))
+	{
+	  /* Delete unnecessary LUI and reuse the reloc.  */
+	  return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4,
+					   link_info, pcgp_relocs, rel);
+	}
+      break;
+
+    default:
+      abort ();
+    }
+
+  return true;
+}
+
+static bool
+riscv_nds_record_pcgp_hi_reloc_ext (riscv_pcgp_relocs *p, bfd_vma hi_sec_off,
+				    bfd_vma hi_addend, bfd_vma hi_addr,
+				    unsigned hi_sym, asection *sym_sec,
+				    bool undefined_weak,
+				    Elf_Internal_Rela *rel)
+{
+  bool rz = riscv_record_pcgp_hi_reloc (p, hi_sec_off, hi_addend, hi_addr,
+					hi_sym, sym_sec, undefined_weak);
+  if (rz)
+    p->hi->rel = rel;
+  return rz;
+}
+
+/* PASS_XANDESPERF_GP1: Modify low part insns. the high part
+   will be removed before riscv_free_pcgp_relocs executes.  */
+static bool
+_bfd_riscv_nds_relax_pc (bfd *abfd,
+			 asection *sec,
+			 asection *sym_sec,
+			 struct bfd_link_info *link_info,
+			 Elf_Internal_Rela *rel,
+			 bfd_vma symval,
+			 bfd_vma max_alignment,
+			 bfd_vma reserve_size,
+			 bool *again ATTRIBUTE_UNUSED,
+			 riscv_pcgp_relocs *pcgp_relocs,
+			 bool undefined_weak)
+{
+  bfd_vma gp = riscv_global_pointer_value (link_info);
+  bfd_vma data_segment_alignment = link_info->relro
+				   ? ELF_MAXPAGESIZE + ELF_COMMONPAGESIZE
+				   : ELF_MAXPAGESIZE;
+
+  BFD_ASSERT (rel->r_offset + 4 <= sec->size);
+
+  /* Chain the _LO relocs to their cooresponding _HI reloc to compute the
+     actual target address.  */
+  riscv_pcgp_hi_reloc hi_reloc;
+  memset (&hi_reloc, 0, sizeof (hi_reloc));
+  bfd_vma hi_sec_off;
+  switch (ELFNN_R_TYPE (rel->r_info))
+    {
+    case R_RISCV_PCREL_LO12_I:
+    case R_RISCV_PCREL_LO12_S:
+      {
+	/* If the %lo has an addend, it isn't for the label pointing at the
+	   hi part instruction, but rather for the symbol pointed at by the
+	   hi part instruction.  So we must subtract it here for the lookup.
+	   It is still used below in the final symbol address.  */
+	hi_sec_off = symval - sec_addr (sym_sec) - rel->r_addend;
+	riscv_pcgp_hi_reloc *hi = riscv_find_pcgp_hi_reloc (pcgp_relocs,
+							    hi_sec_off);
+	if (hi == NULL)
+	  {
+	    riscv_record_pcgp_lo_reloc (pcgp_relocs, hi_sec_off);
+	    return true;
+	  }
+
+	hi_reloc = *hi;
+	symval = hi_reloc.hi_addr;
+	sym_sec = hi_reloc.sym_sec;
+
+	/* We can not know whether the undefined weak symbol is referenced
+	   according to the information of R_RISCV_PCREL_LO12_I/S.  Therefore,
+	   we have to record the 'undefined_weak' flag when handling the
+	   corresponding R_RISCV_HI20 reloc in riscv_record_pcgp_hi_reloc.  */
+	undefined_weak = hi_reloc.undefined_weak;
+      }
+      break;
+
+    case R_RISCV_PCREL_HI20:
+	riscv_nds_record_pcgp_hi_reloc_ext (pcgp_relocs,
+					    rel->r_offset,
+					    rel->r_addend,
+					    symval,
+					    ELFNN_R_SYM (rel->r_info),
+					    sym_sec, undefined_weak, rel);
+	return true;
+      break;
+
+    default:
+      abort ();
+    }
+
+  if (undefined_weak || !gp || sym_sec->output_section == bfd_abs_section_ptr
+      || elf_section_flags (sym_sec) & SHF_EXECINSTR)
+    {
+      riscv_record_pcgp_lo_reloc (pcgp_relocs, hi_sec_off);
+      return true; /* bypass to original handling  */
+    }
+
+  /* If gp and the symbol are in the same output section, which is not the
+     abs section, then consider only that output section's alignment.  */
+  struct bfd_link_hash_entry *h =
+    bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, false, false,
+			  true);
+  if (h->u.def.section->output_section == sym_sec->output_section
+      && sym_sec->output_section != bfd_abs_section_ptr)
+    max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power;
+  else
+    {
+      /* Consider output section alignments which are in [gp-512K, gp+512K).  */
+      max_alignment = _bfd_riscv_nds_get_max_alignment (sec, gp);
+    }
+
+  if (symval < sec_addr (sym_sec)
+      || symval > (sec_addr (sym_sec) + sym_sec->size))
+    max_alignment = data_segment_alignment > max_alignment
+		    ? data_segment_alignment : max_alignment;
+
+  reserve_size += max_alignment;
+  switch (ELFNN_R_TYPE (rel->r_info))
+    {
+    case R_RISCV_PCREL_LO12_I:
+    case R_RISCV_PCREL_LO12_S:
+      {
+	if (!riscv_nds_relax_gp_insn (abfd, sec, sym_sec, rel, symval,
+				      hi_reloc.hi_sym, hi_reloc.hi_addend,
+				      gp, reserve_size))
+	  riscv_record_pcgp_lo_reloc (pcgp_relocs, hi_sec_off);
+      }
+      break;
+
+    default:
+      abort ();
+    }
+
+  return true;
+}
+
+/* Delete the unnecessary AUIPC and reloc.  */
+static void
+riscv_nds_relax_pc_final (riscv_pcgp_relocs *p)
+{
+  riscv_pcgp_hi_reloc *c;
+
+  for (c = p->hi; c != NULL; c = c->next)
+    {
+      if (!riscv_find_pcgp_lo_reloc (p, c->hi_sec_off))
+	{
+	  c->rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE);
+	  c->rel->r_addend = 4;
+	}
+    }
+}
+
 /* Relax a section.
 
-   Pass 0: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and
-	   deletes the obsolete bytes.
-   Pass 1: Which cannot be disabled, handles code alignment directives.  */
+   Pass PASS_XANDESPERF_GP1
+	Stage 1 of Andes GP relaxation. In this stage, the low part of
+	a LUI pair is checked to determine whether it can be shortened
+	using Andes GP instructions. Because different Andes GP
+	instructions have different alignment limits, the high part of
+	the LUI instruction cannot be deleted before all low parts
+	have been checked. The high part of the LUI will therefore
+	remain until Stage 2.
+	On the other hand, the high part of a PC-relative (pcrel)
+	instruction is recorded to verify the low part of the same
+	pcrel instruction in this stage. The relaxation results are
+	stored in pcgp_relocs, which are later used to determine which
+	high parts should be deleted before _bfd_riscv_relax_section
+	returns.
+   Pass PASS_XANDESPERF_GP2
+	Stage 2 of Andes GP relaxation. In this stage, the high parts
+	of LUI are processed using the results from Stage 1 to determine
+	which high parts should be deleted. Additionally, relocations
+	generated by PASS_XANDESPERF_GP1 did not include R_RISCV_VENDOR.
+	R_RISCV_VENDOR relocations are added in this stage.
+   Pass PASS_SHORTEN
+	Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and
+	deletes the obsolete bytes.
+   Pass PASS_ALIGN
+	Which cannot be disabled, handles code alignment directives.  */
 
 static bool
 _bfd_riscv_relax_section (bfd *abfd, asection *sec,
@@ -5471,12 +6057,18 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
       || (sec->flags & SEC_RELOC) == 0
       || (sec->flags & SEC_HAS_CONTENTS) == 0
       || (info->disable_target_specific_optimizations
-	  && info->relax_pass == 0)
+	  && info->relax_pass < PASS_ALIGN)
       /* The exp_seg_relro_adjust is enum phase_enum (0x4),
 	 and defined in ld/ldexp.h.  */
       || *(htab->data_segment_phase) == 4)
     return true;
 
+  if ((info->relax_pass == PASS_XANDESPERF_GP1
+       || info->relax_pass == PASS_XANDESPERF_GP2)
+      && (!riscv_has_subset (info, "xandesperf")
+	  || !htab->params->relax_gp))
+    return true;
+
   /* Record the first relax section, so that we can reset the
      max_alignment_for_gp for the repeated relax passes.  */
   if (first_section == NULL)
@@ -5515,7 +6107,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 
       relax_func = NULL;
       riscv_relax_delete_bytes = NULL;
-      if (info->relax_pass == 0)
+      if (info->relax_pass == PASS_SHORTEN)
 	{
 	  if (type == R_RISCV_CALL
 	      || type == R_RISCV_CALL_PLT)
@@ -5547,11 +6139,88 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 	  /* Skip over the R_RISCV_RELAX.  */
 	  i++;
 	}
-      else if (info->relax_pass == 1 && type == R_RISCV_ALIGN)
+      else if (info->relax_pass == PASS_ALIGN && type == R_RISCV_ALIGN)
 	{
 	  relax_func = _bfd_riscv_relax_align;
 	  riscv_relax_delete_bytes = _riscv_relax_delete_immediate;
 	}
+      else if (info->relax_pass == PASS_XANDESPERF_GP1)
+	{
+	  if (type == R_RISCV_LO12_I
+	      || type == R_RISCV_LO12_S)
+	    relax_func = _bfd_riscv_nds_relax_lui;
+	  else if (!bfd_link_pic (info)
+		   && (type == R_RISCV_PCREL_HI20
+		       || type == R_RISCV_PCREL_LO12_I
+		       || type == R_RISCV_PCREL_LO12_S))
+	    relax_func = _bfd_riscv_nds_relax_pc;
+	  else
+	    continue;
+
+	  /* Only relax this reloc if it is paired with R_RISCV_RELAX.  */
+	  if (i == sec->reloc_count - 1
+	      || ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX
+	      || rel->r_offset != (rel + 1)->r_offset)
+	    continue;
+
+	  /* Skip over the R_RISCV_RELAX.  */
+	  i++;
+	}
+      else if (info->relax_pass == PASS_XANDESPERF_GP2)
+	{
+	  /* Add R_RISCV_VENDOR relocations using the space of original
+	     R_RISCV_RELAX.  */
+	  if (type >= R_RISCV_NDS_GPREL_18S0_I
+	      && type <= R_RISCV_NDS_GPREL_17S3_S
+	      && i != sec->reloc_count - 1
+	      && ELFNN_R_TYPE ((rel + 1)->r_info) == R_RISCV_RELAX
+	      && rel->r_offset == (rel + 1)->r_offset)
+	    {
+	      memcpy (rel + 1, rel, sizeof (Elf_Internal_Rela));
+
+	      if (!symtab_hdr->contents)
+		goto fail;
+
+	      unsigned int symidx = 0;
+	      const char *name = NULL;
+	      bool find = false;
+	      for (; symidx < symtab_hdr->sh_info; symidx++)
+		{
+		  Elf_Internal_Sym *isym =
+		    (Elf_Internal_Sym *) symtab_hdr->contents + symidx;
+		  name = bfd_elf_sym_name (abfd, symtab_hdr, isym, NULL);
+		  if (!strcmp (name, RISCV_VENDOR_STR_NDS))
+		    {
+		      find = true;
+		      break;
+		    }
+		}
+	      if (find)
+		{
+		  rel->r_info = ELFNN_R_INFO (symidx, R_RISCV_VENDOR);
+		  rel->r_addend = 0;
+		}
+	      else
+		BFD_ASSERT (0);
+	      i++;
+	      continue;
+	    }
+
+	  if (type == R_RISCV_HI20)
+	    relax_func = _bfd_riscv_nds_relax_lui;
+	  else
+	    continue;
+	  riscv_relax_delete_bytes = _riscv_relax_delete_piecewise;
+
+	  /* Only relax this reloc if it is paired with R_RISCV_RELAX.  */
+	  if (i == sec->reloc_count - 1
+	      || ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX
+	      || rel->r_offset != (rel + 1)->r_offset)
+	    continue;
+
+	  /* Skip over the R_RISCV_RELAX.  */
+	  i++;
+	}
       else
 	continue;
 
@@ -5714,6 +6383,9 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 		       &pcgp_relocs, undefined_weak))
 	goto fail;
     }
+  /* Delete the unnecessary AUIPC and reloc.  */
+  if (info->relax_pass == PASS_XANDESPERF_GP1)
+    riscv_nds_relax_pc_final (&pcgp_relocs);
 
   /* Resolve R_RISCV_DELETE relocations.  */
   if (!riscv_relax_resolve_delete_relocs (abfd, sec, info, relocs))
@@ -5726,6 +6398,15 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
     free (relocs);
   riscv_free_pcgp_relocs (&pcgp_relocs, abfd, sec);
 
+  /* Free structures.  */
+  if (info->relax_pass == PASS_XANDESPERF_GP2 + 1)
+    while (relax_gp_sym_info_head != NULL)
+      {
+	struct relax_gp_sym_info *temp = relax_gp_sym_info_head;
+	relax_gp_sym_info_head = relax_gp_sym_info_head->next;
+	free (temp);
+      }
+
   return ret;
 }
 
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index 1a391c51f2b..731ce443570 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -1009,10 +1009,165 @@ static const reloc_howto_type howto_table_internal[] =
 	 false),			/* pcrel_offset */
 };
 
+static reloc_howto_type howto_table_nds[] =
+{
+  EMPTY_HOWTO (192), EMPTY_HOWTO (193), EMPTY_HOWTO (194), EMPTY_HOWTO (195),
+  EMPTY_HOWTO (196), EMPTY_HOWTO (197), EMPTY_HOWTO (198), EMPTY_HOWTO (199),
+  EMPTY_HOWTO (200), EMPTY_HOWTO (201), EMPTY_HOWTO (202), EMPTY_HOWTO (203),
+  EMPTY_HOWTO (204), EMPTY_HOWTO (205), EMPTY_HOWTO (206), EMPTY_HOWTO (207),
+  EMPTY_HOWTO (208), EMPTY_HOWTO (209), EMPTY_HOWTO (210), EMPTY_HOWTO (211),
+  EMPTY_HOWTO (212), EMPTY_HOWTO (213), EMPTY_HOWTO (214), EMPTY_HOWTO (215),
+  EMPTY_HOWTO (216), EMPTY_HOWTO (217), EMPTY_HOWTO (218), EMPTY_HOWTO (219),
+  EMPTY_HOWTO (220), EMPTY_HOWTO (221), EMPTY_HOWTO (222), EMPTY_HOWTO (223),
+  EMPTY_HOWTO (224), EMPTY_HOWTO (225), EMPTY_HOWTO (226), EMPTY_HOWTO (227),
+  EMPTY_HOWTO (228), EMPTY_HOWTO (229), EMPTY_HOWTO (230), EMPTY_HOWTO (231),
+  EMPTY_HOWTO (232), EMPTY_HOWTO (233), EMPTY_HOWTO (234), EMPTY_HOWTO (235),
+  EMPTY_HOWTO (236), EMPTY_HOWTO (237), EMPTY_HOWTO (238), EMPTY_HOWTO (239),
+  EMPTY_HOWTO (240),
+
+  /* 10-bit PC-relative branch offset.  */
+  HOWTO (R_RISCV_NDS_BRANCH_10,		/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 true,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_NDS_BRANCH_10",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_NDS_BRANCH_IMM10 (-1U),	/* dst_mask */
+	 true),				/* pcrel_offset */
+
+  EMPTY_HOWTO (242), EMPTY_HOWTO (243), EMPTY_HOWTO (244), EMPTY_HOWTO (245),
+
+  /* 18-bit GP-relative load offset.  */
+  HOWTO (R_RISCV_NDS_GPREL_18S0_I,	/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_NDS_GPREL_18S0_I",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_NDS_LBGP_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* 17-bit and right shift 1-bit GP-relative load offset.  */
+  HOWTO (R_RISCV_NDS_GPREL_17S1_I,	/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_NDS_GPREL_17S1_I",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_NDS_LHGP_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* 16-bit and right shift 2-bit GP-relative load offset.  */
+  HOWTO (R_RISCV_NDS_GPREL_17S2_I,	/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_NDS_GPREL_17S2_I",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_NDS_LWGP_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* 15-bit and right shift 3-bit GP-relative load offset.  */
+  HOWTO (R_RISCV_NDS_GPREL_17S3_I,	/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_NDS_GPREL_17S3_I",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_NDS_LDGP_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* 18-bit GP-relative store offset.  */
+  HOWTO (R_RISCV_NDS_GPREL_18S0_S,	/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_NDS_GPREL_18S0_S",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_NDS_SBGP_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* 17-bit and right shift 1-bit GP-relative store offset.  */
+  HOWTO (R_RISCV_NDS_GPREL_17S1_S,	/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_NDS_GPREL_17S1_S",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_NDS_SHGP_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* 16-bit and right shift 2-bit GP-relative store offset.  */
+  HOWTO (R_RISCV_NDS_GPREL_17S2_S,	/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_NDS_GPREL_17S2_S",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_NDS_SWGP_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* 15-bit and right shift 3-bit GP-relative store offset.  */
+  HOWTO (R_RISCV_NDS_GPREL_17S3_S,	/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_signed,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_NDS_GPREL_17S3_S",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_NDS_SDGP_IMM (-1U),	/* dst_mask */
+	 false)				/* pcrel_offset */
+};
+
 static const reloc_howto_type *const howto_table_vendors[] =
 {
   /* Register vendor howto tables here. The order must match the order of
      strings in the vendor string table.  */
+  howto_table_nds,
   NULL
 };
 
@@ -1020,6 +1175,7 @@ static const size_t howto_table_vendor_sizes[] =
 {
   /* Register vendor howto table sizes here. The order must match the order of
      strings in the vendor string table.  */
+  ARRAY_SIZE (howto_table_nds),
   0
 };
 
@@ -1086,10 +1242,24 @@ static const struct elf_reloc_map riscv_reloc_map[] =
   { BFD_RELOC_RISCV_VENDOR, R_RISCV_VENDOR },
 };
 
+static const struct elf_reloc_map riscv_nds_reloc_map[] =
+{
+  { BFD_RELOC_RISCV_NDS_BRANCH_10, R_RISCV_NDS_BRANCH_10 },
+  { BFD_RELOC_RISCV_NDS_GPREL_18S0_I, R_RISCV_NDS_GPREL_18S0_I },
+  { BFD_RELOC_RISCV_NDS_GPREL_17S1_I, R_RISCV_NDS_GPREL_17S1_I },
+  { BFD_RELOC_RISCV_NDS_GPREL_17S2_I, R_RISCV_NDS_GPREL_17S2_I },
+  { BFD_RELOC_RISCV_NDS_GPREL_17S3_I, R_RISCV_NDS_GPREL_17S3_I },
+  { BFD_RELOC_RISCV_NDS_GPREL_18S0_S, R_RISCV_NDS_GPREL_18S0_S },
+  { BFD_RELOC_RISCV_NDS_GPREL_17S1_S, R_RISCV_NDS_GPREL_17S1_S },
+  { BFD_RELOC_RISCV_NDS_GPREL_17S2_S, R_RISCV_NDS_GPREL_17S2_S },
+  { BFD_RELOC_RISCV_NDS_GPREL_17S3_S, R_RISCV_NDS_GPREL_17S3_S },
+};
+
 static const struct elf_reloc_map *const riscv_vendor_reloc_maps[] =
 {
   /* Register vendor relocation maps here. The order must match the order of
      strings in the vendor string table.  */
+  riscv_nds_reloc_map,
   NULL
 };
 
@@ -1097,13 +1267,20 @@ static const size_t riscv_vendor_reloc_map_sizes[] =
 {
   /* Register vendor relocation map sizes here. The order must match the order
      of strings in the vendor string table.  */
+  ARRAY_SIZE (riscv_nds_reloc_map),
   0
 };
 
+static const char *const riscv_nds_ext_with_reloc_table[] =
+{
+  "xandesperf"
+};
+
 static const char *const *const riscv_vendor_ext_with_reloc_tables[] =
 {
   /* Register vendor extension-with-relocation tables here. The order must
      match the order of strings in the vendor string table.  */
+  riscv_nds_ext_with_reloc_table,
   NULL
 };
 
@@ -1111,6 +1288,7 @@ static const size_t riscv_vendor_ext_with_reloc_table_sizes[] =
 {
   /* Register vendor extension-with-relocation table sizes here. The order must
      match the order of strings in the vendor string table.  */
+  ARRAY_SIZE (riscv_nds_ext_with_reloc_table),
   0
 };
 
@@ -1783,6 +1961,7 @@ static const struct riscv_supported_ext riscv_supported_std_zxm_ext[] =
 
 static const struct riscv_supported_ext riscv_supported_vendor_x_ext[] =
 {
+  {"xandesperf",	ISA_SPEC_CLASS_DRAFT,	5, 0, 0 },
   {"xcvalu",		ISA_SPEC_CLASS_DRAFT,	1, 0, 0 },
   {"xcvbi",		ISA_SPEC_CLASS_DRAFT,	1, 0, 0 },
   {"xcvbitmanip",	ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
@@ -3190,6 +3369,8 @@ riscv_multi_subset_supports (riscv_parse_subset_t *rps,
       return riscv_subset_supports (rps, "svinval");
     case INSN_CLASS_H:
       return riscv_subset_supports (rps, "h");
+    case INSN_CLASS_XANDESPERF:
+      return riscv_subset_supports (rps, "xandesperf");
     case INSN_CLASS_XCVALU:
       return riscv_subset_supports (rps, "xcvalu");
     case INSN_CLASS_XCVBI:
@@ -3492,6 +3673,8 @@ riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
       return "svinval";
     case INSN_CLASS_H:
       return _("h");
+    case INSN_CLASS_XANDESPERF:
+      return "xandesperf";
     case INSN_CLASS_XCVALU:
       return "xcvalu";
     case INSN_CLASS_XCVBI:
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index 41631ad4465..f6080c99d5c 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -35,6 +35,14 @@ typedef enum
 
 enum elf_riscv_vendor_id;
 
+enum relax_pass
+{
+  PASS_XANDESPERF_GP1,	/* Stage 1 of XAndesPerf Relaxation  */
+  PASS_XANDESPERF_GP2,	/* Stage 2 of XAndesPerf Relaxation  */
+  PASS_SHORTEN,		/* Shortens code sequences.  */
+  PASS_ALIGN		/* Handles code alignment.  */
+};
+
 struct riscv_elf_params
 {
   /* Whether to relax code sequences to GP-relative addressing.  */
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 9da65b4ec9d..b1ed38e738d 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -2465,6 +2465,15 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_RISCV_SET_ULEB128",
   "BFD_RELOC_RISCV_SUB_ULEB128",
   "BFD_RELOC_RISCV_VENDOR",
+  "BFD_RELOC_RISCV_NDS_BRANCH_10",
+  "BFD_RELOC_RISCV_NDS_GPREL_18S0_I",
+  "BFD_RELOC_RISCV_NDS_GPREL_17S1_I",
+  "BFD_RELOC_RISCV_NDS_GPREL_17S2_I",
+  "BFD_RELOC_RISCV_NDS_GPREL_17S3_I",
+  "BFD_RELOC_RISCV_NDS_GPREL_18S0_S",
+  "BFD_RELOC_RISCV_NDS_GPREL_17S1_S",
+  "BFD_RELOC_RISCV_NDS_GPREL_17S2_S",
+  "BFD_RELOC_RISCV_NDS_GPREL_17S3_S",
   "BFD_RELOC_RL78_NEG8",
   "BFD_RELOC_RL78_NEG16",
   "BFD_RELOC_RL78_NEG24",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 0ef5e550403..3b9514b6644 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -5041,6 +5041,27 @@ ENUMX
 ENUMDOC
   RISC-V relocations.
 
+ENUM
+  BFD_RELOC_RISCV_NDS_BRANCH_10
+ENUMX
+  BFD_RELOC_RISCV_NDS_GPREL_18S0_I
+ENUMX
+  BFD_RELOC_RISCV_NDS_GPREL_17S1_I
+ENUMX
+  BFD_RELOC_RISCV_NDS_GPREL_17S2_I
+ENUMX
+  BFD_RELOC_RISCV_NDS_GPREL_17S3_I
+ENUMX
+  BFD_RELOC_RISCV_NDS_GPREL_18S0_S
+ENUMX
+  BFD_RELOC_RISCV_NDS_GPREL_17S1_S
+ENUMX
+  BFD_RELOC_RISCV_NDS_GPREL_17S2_S
+ENUMX
+  BFD_RELOC_RISCV_NDS_GPREL_17S3_S
+ENUMDOC
+  RISC-V vendor (Andes) relocations.
+
 ENUM
   BFD_RELOC_RL78_NEG8
 ENUMX
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 35deb8dcdae..d200e138b13 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -489,12 +489,14 @@ enum relax_branch_reloc
 {
   RELOC_INVALID = 0,
   RELOC_BRANCH,
-  RELOC_JAL
+  RELOC_JAL,
+  RELOC_NDS_BRANCH_10
 };
 
-#define ENUM_RELAX_BRANCH_RELOC(reloc)			\
-  ((reloc) == BFD_RELOC_12_PCREL    ? RELOC_BRANCH	\
-   : (reloc) == BFD_RELOC_RISCV_JMP ? RELOC_JAL		\
+#define ENUM_RELAX_BRANCH_RELOC(reloc)					\
+  ((reloc) == BFD_RELOC_12_PCREL    ? RELOC_BRANCH			\
+   : (reloc) == BFD_RELOC_RISCV_JMP ? RELOC_JAL				\
+   : (reloc) == BFD_RELOC_RISCV_NDS_BRANCH_10 ? RELOC_NDS_BRANCH_10	\
 				    : RELOC_INVALID)
 
 /* Is the given value a sign-extended 32-bit value?  */
@@ -864,6 +866,11 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update)
 	    length = 2;
 	  break;
 	/* Add new branch/jump cases here.  */
+	case RELOC_NDS_BRANCH_10:
+	  if ((bfd_vma)(val + RISCV_NDS_BRANCH_10_REACH / 2)
+	      < RISCV_NDS_BRANCH_10_REACH)
+	    length = 4;
+	  break;
 	default:
 	  abort ();
 	}
@@ -1705,6 +1712,66 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
 	case 'X': /* Vendor-specific operands.  */
 	  switch (*++oparg)
 	    {
+	    case 'a': /* Vendor-specific (Andes) operands.  */
+	      switch (*++oparg)
+		{
+		case 'g':
+		  used_bits |= ENCODE_NDS_BRANCH_IMM10 (-1U);
+		  break;
+		case 'h':
+		  used_bits |= ENCODE_NDS_BFO_IMM6H (-1U);
+		  break;
+		case 'i':
+		  used_bits |= ENCODE_NDS_BRANCH_IMM7 (-1U);
+		  break;
+		case 'k':
+		  used_bits |= ENCODE_NDS_BRANCH_IMM6 (-1U);
+		  break;
+		case 'l':
+		  used_bits |= ENCODE_NDS_BFO_IMM6L (-1U);
+		  break;
+		case 'G':
+		  switch (*++oparg)
+		    {
+		    case 'b':
+		      used_bits |= ENCODE_NDS_LBGP_IMM (-1U);
+		      break;
+		    case 'h':
+		      used_bits |= ENCODE_NDS_LHGP_IMM (-1U);
+		      break;
+		    case 'w':
+		      used_bits |= ENCODE_NDS_LWGP_IMM (-1U);
+		      break;
+		    case 'd':
+		      used_bits |= ENCODE_NDS_LDGP_IMM (-1U);
+		      break;
+		    default:
+		      goto unknown_validate_operand;
+		    }
+		  break;
+		case 'H':
+		  switch (*++oparg)
+		    {
+		    case 'b':
+		      used_bits |= ENCODE_NDS_SBGP_IMM (-1U);
+		      break;
+		    case 'h':
+		      used_bits |= ENCODE_NDS_SHGP_IMM (-1U);
+		      break;
+		    case 'w':
+		      used_bits |= ENCODE_NDS_SWGP_IMM (-1U);
+		      break;
+		    case 'd':
+		      used_bits |= ENCODE_NDS_SDGP_IMM (-1U);
+		      break;
+		    default:
+		      goto unknown_validate_operand;
+		    }
+		  break;
+		default:
+		  goto unknown_validate_operand;
+		}
+	      break;
 	    case 't': /* Vendor-specific (T-head) operands.  */
 	      {
 		size_t n;
@@ -2028,7 +2095,8 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
 
       gas_assert (address_expr);
       if (reloc_type == BFD_RELOC_12_PCREL
-	  || reloc_type == BFD_RELOC_RISCV_JMP)
+	  || reloc_type == BFD_RELOC_RISCV_JMP
+	  || reloc_type == BFD_RELOC_RISCV_NDS_BRANCH_10)
 	{
 	  int best_case = insn_length (ip);
 	  unsigned worst_case = relaxed_branch_length (NULL, NULL, 0);
@@ -4045,6 +4113,192 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
 	    case 'X': /* Vendor-specific operands.  */
 	      switch (*++oparg)
 		{
+		case 'a': /* Vendor-specific (Andes) operands.  */
+		  switch (*++oparg)
+		    {
+		    case 'g':
+		      my_getExpression (imm_expr, asarg, force_reloc);
+		      *imm_reloc = BFD_RELOC_RISCV_NDS_BRANCH_10;
+		      asarg = expr_parse_end;
+		      continue;
+		    case 'h':
+		      my_getExpression (imm_expr, asarg, force_reloc);
+		      if (imm_expr->X_op != O_constant
+			  || imm_expr->X_add_number >= xlen
+			  || imm_expr->X_add_number < 0)
+			break;
+		      ip->insn_opcode
+			  |= ENCODE_NDS_BFO_IMM6H (imm_expr->X_add_number);
+		      imm_expr->X_op = O_absent;
+		      asarg = expr_parse_end;
+		      continue;
+		    case 'i':
+		      my_getExpression (imm_expr, asarg, force_reloc);
+		      if (imm_expr->X_op != O_constant
+			  || imm_expr->X_add_number
+				 >= (signed)RISCV_NDS_IMM7_REACH
+			  || imm_expr->X_add_number < 0)
+			break;
+		      ip->insn_opcode
+			  |= ENCODE_NDS_BRANCH_IMM7 (imm_expr->X_add_number);
+		      imm_expr->X_op = O_absent;
+		      asarg = expr_parse_end;
+		      continue;
+		    case 'k':
+		      my_getExpression (imm_expr, asarg, force_reloc);
+		      if (imm_expr->X_op != O_constant
+			  || imm_expr->X_add_number >= xlen
+			  || imm_expr->X_add_number < 0)
+			break;
+		      ip->insn_opcode
+			  |= ENCODE_NDS_BRANCH_IMM6 (imm_expr->X_add_number);
+		      imm_expr->X_op = O_absent;
+		      asarg = expr_parse_end;
+		      continue;
+		    case 'l':
+		      my_getExpression (imm_expr, asarg, force_reloc);
+		      if (imm_expr->X_op != O_constant
+			  || imm_expr->X_add_number >= xlen
+			  || imm_expr->X_add_number < 0)
+			break;
+		      ip->insn_opcode
+			  |= ENCODE_NDS_BFO_IMM6L (imm_expr->X_add_number);
+		      imm_expr->X_op = O_absent;
+		      asarg = expr_parse_end;
+		      continue;
+		    case 'G':
+		    case 'H':
+		      {
+			bool store = false;
+			if (*oparg == 'H')
+			  store = true;
+			my_getExpression (imm_expr, asarg, force_reloc);
+			switch (*++oparg)
+			  {
+			  case 'b':
+			    if (imm_expr->X_op == O_constant
+				&& imm_expr->X_add_number
+				       < (signed)RISCV_NDS_IMM18_REACH / 2
+				&& imm_expr->X_add_number
+				       >= -(signed)RISCV_NDS_IMM18_REACH / 2)
+			      {
+				if (store)
+				  ip->insn_opcode |= ENCODE_NDS_SBGP_IMM (
+				      imm_expr->X_add_number);
+				else
+				  ip->insn_opcode |= ENCODE_NDS_LBGP_IMM (
+				      imm_expr->X_add_number);
+				asarg = expr_parse_end;
+				continue;
+			      }
+			    else if (imm_expr->X_op == O_symbol)
+			      {
+				if (store)
+				  *imm_reloc
+				      = BFD_RELOC_RISCV_NDS_GPREL_18S0_S;
+				else
+				  *imm_reloc
+				      = BFD_RELOC_RISCV_NDS_GPREL_18S0_I;
+				asarg = expr_parse_end;
+				continue;
+			      }
+			    break;
+			  case 'h':
+			    if (imm_expr->X_op == O_constant
+				&& imm_expr->X_add_number
+				       < (signed)RISCV_NDS_IMM18_REACH / 2
+				&& imm_expr->X_add_number
+				       >= -(signed)RISCV_NDS_IMM18_REACH / 2
+				&& (imm_expr->X_add_number & 0x1) == 0)
+			      {
+				if (store)
+				  ip->insn_opcode |= ENCODE_NDS_SHGP_IMM (
+				      imm_expr->X_add_number);
+				else
+				  ip->insn_opcode |= ENCODE_NDS_LHGP_IMM (
+				      imm_expr->X_add_number);
+				asarg = expr_parse_end;
+				continue;
+			      }
+			    else if (imm_expr->X_op == O_symbol)
+			      {
+				if (store)
+				  *imm_reloc
+				      = BFD_RELOC_RISCV_NDS_GPREL_17S1_S;
+				else
+				  *imm_reloc
+				      = BFD_RELOC_RISCV_NDS_GPREL_17S1_I;
+				asarg = expr_parse_end;
+				continue;
+			      }
+			    break;
+			  case 'w':
+			    if (imm_expr->X_op == O_constant
+				&& imm_expr->X_add_number
+				       < (signed)RISCV_NDS_IMM19_REACH / 2
+				&& imm_expr->X_add_number
+				       >= -(signed)RISCV_NDS_IMM19_REACH / 2
+				&& (imm_expr->X_add_number & 0x3) == 0)
+			      {
+				if (store)
+				  ip->insn_opcode |= ENCODE_NDS_SWGP_IMM (
+				      imm_expr->X_add_number);
+				else
+				  ip->insn_opcode |= ENCODE_NDS_LWGP_IMM (
+				      imm_expr->X_add_number);
+				asarg = expr_parse_end;
+				continue;
+			      }
+			    else if (imm_expr->X_op == O_symbol)
+			      {
+				if (store)
+				  *imm_reloc
+				      = BFD_RELOC_RISCV_NDS_GPREL_17S2_S;
+				else
+				  *imm_reloc
+				      = BFD_RELOC_RISCV_NDS_GPREL_17S2_I;
+				asarg = expr_parse_end;
+				continue;
+			      }
+			    break;
+			  case 'd':
+			    if (imm_expr->X_op == O_constant
+				&& imm_expr->X_add_number
+				       < (signed)RISCV_NDS_IMM20_REACH / 2
+				&& imm_expr->X_add_number
+				       >= -(signed)RISCV_NDS_IMM20_REACH / 2
+				&& (imm_expr->X_add_number & 0x7) == 0)
+			      {
+				if (store)
+				  ip->insn_opcode |= ENCODE_NDS_SDGP_IMM (
+				      imm_expr->X_add_number);
+				else
+				  ip->insn_opcode |= ENCODE_NDS_LDGP_IMM (
+				      imm_expr->X_add_number);
+				asarg = expr_parse_end;
+				continue;
+			      }
+			    else if (imm_expr->X_op == O_symbol)
+			      {
+				if (store)
+				  *imm_reloc
+				      = BFD_RELOC_RISCV_NDS_GPREL_17S3_S;
+				else
+				  *imm_reloc
+				      = BFD_RELOC_RISCV_NDS_GPREL_17S3_I;
+				asarg = expr_parse_end;
+				continue;
+			      }
+			    break;
+			  default:
+			    goto unknown_riscv_ip_operand;
+			  }
+			break;
+		      }
+		    default:
+		      goto unknown_riscv_ip_operand;
+		    }
+		  break;
 		case 't': /* Vendor-specific (T-head) operands.  */
 		  {
 		    size_t n;
@@ -5084,6 +5338,34 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
     case BFD_RELOC_RISCV_VENDOR:
       break;
 
+    case BFD_RELOC_RISCV_NDS_BRANCH_10:
+      if (fixP->fx_addsy)
+	{
+	  /* Fill in a tentative value to improve objdump readability.  */
+	  bfd_vma target = S_GET_VALUE (fixP->fx_addsy) + *valP;
+	  bfd_vma delta = target - md_pcrel_from (fixP);
+	  bfd_putl32 (bfd_getl32 (buf) | ENCODE_NDS_BRANCH_IMM10 (delta), buf);
+	  if (!riscv_opts.relax && S_IS_LOCAL (fixP->fx_addsy))
+	    {
+	      if (!VALID_NDS_BRANCH_IMM10 (delta))
+		as_bad_where (fixP->fx_file, fixP->fx_line,
+			      _("invalid NDS_BRANCH_10 offset (%+lld)"),
+			      (long long)delta);
+	      fixP->fx_done = 1;
+	    }
+	}
+      break;
+
+    case BFD_RELOC_RISCV_NDS_GPREL_18S0_I:
+    case BFD_RELOC_RISCV_NDS_GPREL_17S1_I:
+    case BFD_RELOC_RISCV_NDS_GPREL_17S2_I:
+    case BFD_RELOC_RISCV_NDS_GPREL_17S3_I:
+    case BFD_RELOC_RISCV_NDS_GPREL_18S0_S:
+    case BFD_RELOC_RISCV_NDS_GPREL_17S1_S:
+    case BFD_RELOC_RISCV_NDS_GPREL_17S2_S:
+    case BFD_RELOC_RISCV_NDS_GPREL_17S3_S:
+      break;
+
     default:
       /* We ignore generic BFD relocations we don't know about.  */
       if (bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type) != NULL)
@@ -5575,6 +5857,18 @@ md_convert_frag_branch (fragS *fragp)
 	  insn ^= MATCH_BEQ ^ MATCH_BNE;
 	  insn |= ENCODE_BTYPE_IMM (8);
 	}
+      else if (((insn & MASK_NDS_BEQC) == MATCH_NDS_BEQC)
+	       || ((insn & MASK_NDS_BNEC) == MATCH_NDS_BNEC))
+	{
+	  insn ^= MATCH_NDS_BEQC ^ MATCH_NDS_BNEC;
+	  insn |= ENCODE_NDS_BRANCH_IMM10 (8);
+	}
+      else if (((insn & MASK_NDS_BBC) == MATCH_NDS_BBC)
+	       || ((insn & MASK_NDS_BBS) == MATCH_NDS_BBS))
+	{
+	  insn ^= MATCH_NDS_BBC ^ MATCH_NDS_BBS;
+	  insn |= ENCODE_NDS_BRANCH_IMM10 (8);
+	}
       /* Add new branch cases here.  */
       else
 	abort ();
@@ -5599,6 +5893,9 @@ md_convert_frag_branch (fragS *fragp)
 	  reloc = BFD_RELOC_RISCV_JMP;
 	  break;
 	/* Add new branch/jump cases here.  */
+	case RELOC_NDS_BRANCH_10:
+	  reloc = BFD_RELOC_RISCV_NDS_BRANCH_10;
+	  break;
 	default:
 	  abort ();
 	}
diff --git a/gas/doc/c-riscv.texi b/gas/doc/c-riscv.texi
index ea4be32a8ff..9e16a8e8848 100644
--- a/gas/doc/c-riscv.texi
+++ b/gas/doc/c-riscv.texi
@@ -767,6 +767,11 @@ extensions supported and provides the location of their
 publicly-released documentation:
 
 @table @r
+@item XAndesPerf
+The XAndesPerf extension provides performance enhancement instructions, including branch instructions, load effective address instructions, GP-relative instructions, and string processing instructions.
+
+It is documented in @url{https://github.com/andestech/andes-v5-isa/releases/download/ast-v5_4_0-release/AndeStar_V5_ISA_Spec_UM165-v1.5.08-20250317.pdf}.
+
 @item XCvAlu
 The XCvAlu extension provides instructions for general ALU operations.
 
diff --git a/gas/testsuite/gas/riscv/march-help.l b/gas/testsuite/gas/riscv/march-help.l
index 0ce2f896735..f62aac96242 100644
--- a/gas/testsuite/gas/riscv/march-help.l
+++ b/gas/testsuite/gas/riscv/march-help.l
@@ -154,6 +154,7 @@ All available -march extensions for RISC-V:
 	smmpm                                   1.0
 	sspm                                    1.0
 	supm                                    1.0
+	xandesperf                              5.0
 	xcvalu                                  1.0
 	xcvbi                                   1.0
 	xcvbitmanip                             1.0
diff --git a/gas/testsuite/gas/riscv/x-andes-perf.d b/gas/testsuite/gas/riscv/x-andes-perf.d
new file mode 100644
index 00000000000..2fe0c3ac905
--- /dev/null
+++ b/gas/testsuite/gas/riscv/x-andes-perf.d
@@ -0,0 +1,107 @@
+#as: -march=rv64i_xandesperf
+#source: x-andes-perf.s
+#objdump: -dr
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text:
+
+0+000 <target>:
+[ 	]+[0-9a-f]+:[ 	]+0005705b[ 	]+nds\.bbc[ 	]+a0,0,0 <target>
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_BRANCH_10[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+4005745b[ 	]+nds\.bbs[ 	]+a0,0,c.*
+[ 	]+[0-9a-f]+:[ 	]+ff9ff06f[ 	]+j[ 	]+0.*
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_JAL[ 	]far
+[ 	]+[0-9a-f]+:[ 	]+4005745b[ 	]+nds\.bbs[ 	]+a0,0,14.*
+[ 	]+[0-9a-f]+:[ 	]+0000006f[ 	]+j[ 	]+10.*
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_JAL[ 	]\*ABS\*\+0x1000
+[ 	]+[0-9a-f]+:[ 	]+fe05765b[ 	]+nds\.bbs[ 	]+a0,0,0 <target>
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_BRANCH_10[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+0005745b[ 	]+nds\.bbc[ 	]+a0,0,20.*
+[ 	]+[0-9a-f]+:[ 	]+fe5ff06f[ 	]+j[ 	]+0.*
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_JAL[ 	]far
+[ 	]+[0-9a-f]+:[ 	]+0005745b[ 	]+nds\.bbc[ 	]+a0,0,28.*
+[ 	]+[0-9a-f]+:[ 	]+0000006f[ 	]+j[ 	]+24.*
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_JAL[ 	]\*ABS\*\+0x1000
+[ 	]+[0-9a-f]+:[ 	]+bc055c5b[ 	]+nds\.beqc[ 	]+a0,0,0 <target>
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_BRANCH_10[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+0005645b[ 	]+nds\.bnec[ 	]+a0,0,34.*
+[ 	]+[0-9a-f]+:[ 	]+fd1ff06f[ 	]+j[ 	]+0.*
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_JAL[ 	]far
+[ 	]+[0-9a-f]+:[ 	]+0005645b[ 	]+nds\.bnec[ 	]+a0,0,3c.*
+[ 	]+[0-9a-f]+:[ 	]+0000006f[ 	]+j[ 	]+38.*
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_JAL[ 	]\*ABS\*\+0x1000
+[ 	]+[0-9a-f]+:[ 	]+bc05625b[ 	]+nds\.bnec[ 	]+a0,0,0 <target>
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_BRANCH_10[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+0005545b[ 	]+nds\.beqc[ 	]+a0,0,48.*
+[ 	]+[0-9a-f]+:[ 	]+fbdff06f[ 	]+j[ 	]+0.*
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_JAL[ 	]far
+[ 	]+[0-9a-f]+:[ 	]+0005545b[ 	]+nds\.beqc[ 	]+a0,0,50.*
+[ 	]+[0-9a-f]+:[ 	]+0000006f[ 	]+j[ 	]+4c.*
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_JAL[ 	]\*ABS\*\+0x1000
+[ 	]+[0-9a-f]+:[ 	]+0005b55b[ 	]+nds\.bfos[ 	]+a0,a1,0,0
+[ 	]+[0-9a-f]+:[ 	]+0005a55b[ 	]+nds\.bfoz[ 	]+a0,a1,0,0
+[ 	]+[0-9a-f]+:[ 	]+0ac5855b[ 	]+nds\.lea\.h[ 	]+a0,a1,a2
+[ 	]+[0-9a-f]+:[ 	]+0cc5855b[ 	]+nds\.lea\.w[ 	]+a0,a1,a2
+[ 	]+[0-9a-f]+:[ 	]+0ec5855b[ 	]+nds\.lea\.d[ 	]+a0,a1,a2
+[ 	]+[0-9a-f]+:[ 	]+10c5855b[ 	]+nds\.lea\.b\.ze[ 	]+a0,a1,a2
+[ 	]+[0-9a-f]+:[ 	]+12c5855b[ 	]+nds\.lea\.h\.ze[ 	]+a0,a1,a2
+[ 	]+[0-9a-f]+:[ 	]+14c5855b[ 	]+nds\.lea\.w\.ze[ 	]+a0,a1,a2
+[ 	]+[0-9a-f]+:[ 	]+16c5855b[ 	]+nds\.lea\.d\.ze[ 	]+a0,a1,a2
+[ 	]+[0-9a-f]+:[ 	]+0000150b[ 	]+nds\.addigp[ 	]+a0,0
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_GPREL_18S0_I[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+0002150b[ 	]+nds\.addigp[ 	]+a0,4096
+[ 	]+[0-9a-f]+:[ 	]+0000050b[ 	]+nds\.lbgp[ 	]+a0,0
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_GPREL_18S0_I[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+0002050b[ 	]+nds\.lbgp[ 	]+a0,4096
+[ 	]+[0-9a-f]+:[ 	]+0000250b[ 	]+nds\.lbugp[ 	]+a0,0
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_GPREL_18S0_I[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+0002250b[ 	]+nds\.lbugp[ 	]+a0,4096
+[ 	]+[0-9a-f]+:[ 	]+0000152b[ 	]+nds\.lhgp[ 	]+a0,0
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_GPREL_17S1_I[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+0002152b[ 	]+nds\.lhgp[ 	]+a0,4096
+[ 	]+[0-9a-f]+:[ 	]+0000552b[ 	]+nds\.lhugp[ 	]+a0,0
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_GPREL_17S1_I[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+0002552b[ 	]+nds\.lhugp[ 	]+a0,4096
+[ 	]+[0-9a-f]+:[ 	]+0000252b[ 	]+nds\.lwgp[ 	]+a0,0
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_GPREL_17S2_I[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+0002252b[ 	]+nds\.lwgp[ 	]+a0,4096
+[ 	]+[0-9a-f]+:[ 	]+0000652b[ 	]+nds\.lwugp[ 	]+a0,0
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_GPREL_17S2_I[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+0002652b[ 	]+nds\.lwugp[ 	]+a0,4096
+[ 	]+[0-9a-f]+:[ 	]+0000352b[ 	]+nds\.ldgp[ 	]+a0,0
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_GPREL_17S3_I[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+0002352b[ 	]+nds\.ldgp[ 	]+a0,4096
+[ 	]+[0-9a-f]+:[ 	]+00a0300b[ 	]+nds\.sbgp[ 	]+a0,0
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_GPREL_18S0_S[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+00a2300b[ 	]+nds\.sbgp[ 	]+a0,4096
+[ 	]+[0-9a-f]+:[ 	]+00a0002b[ 	]+nds\.shgp[ 	]+a0,0
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_GPREL_17S1_S[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+00a2002b[ 	]+nds\.shgp[ 	]+a0,4096
+[ 	]+[0-9a-f]+:[ 	]+00a0402b[ 	]+nds\.swgp[ 	]+a0,0
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_GPREL_17S2_S[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+00a2402b[ 	]+nds\.swgp[ 	]+a0,4096
+[ 	]+[0-9a-f]+:[ 	]+00a0702b[ 	]+nds\.sdgp[ 	]+a0,0
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_VENDOR[ 	]ANDES 
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NDS_GPREL_17S3_S[ 	]target
+[ 	]+[0-9a-f]+:[ 	]+00a2702b[ 	]+nds\.sdgp[ 	]+a0,4096
+[ 	]+[0-9a-f]+:[ 	]+20c5855b[ 	]+nds\.ffb[ 	]+a0,a1,a2
+[ 	]+[0-9a-f]+:[ 	]+22c5855b[ 	]+nds\.ffzmism[ 	]+a0,a1,a2
+[ 	]+[0-9a-f]+:[ 	]+24c5855b[ 	]+nds\.ffmism[ 	]+a0,a1,a2
+[ 	]+[0-9a-f]+:[ 	]+26c5855b[ 	]+nds\.flmism[ 	]+a0,a1,a2
diff --git a/gas/testsuite/gas/riscv/x-andes-perf.s b/gas/testsuite/gas/riscv/x-andes-perf.s
new file mode 100644
index 00000000000..9da87b41ce7
--- /dev/null
+++ b/gas/testsuite/gas/riscv/x-andes-perf.s
@@ -0,0 +1,50 @@
+target:
+	nds.bbc a0, 0, target
+	nds.bbc a0, 0, far
+	nds.bbc a0, 0, 0x1000
+	nds.bbs a0, 0, target
+	nds.bbs a0, 0, far
+	nds.bbs a0, 0, 0x1000
+	nds.beqc a0, 0, target
+	nds.beqc a0, 0, far
+	nds.beqc a0, 0, 0x1000
+	nds.bnec a0, 0, target
+	nds.bnec a0, 0, far
+	nds.bnec a0, 0, 0x1000
+	nds.bfos a0, a1, 0, 0
+	nds.bfoz a0, a1, 0, 0
+	nds.lea.h a0, a1, a2
+	nds.lea.w a0, a1, a2
+	nds.lea.d a0, a1, a2
+	nds.lea.b.ze a0, a1, a2
+	nds.lea.h.ze a0, a1, a2
+	nds.lea.w.ze a0, a1, a2
+	nds.lea.d.ze a0, a1, a2
+	nds.addigp a0, target
+	nds.addigp a0, 0x1000
+	nds.lbgp a0, target
+	nds.lbgp a0, 0x1000
+	nds.lbugp a0, target
+	nds.lbugp a0, 0x1000
+	nds.lhgp a0, target
+	nds.lhgp a0, 0x1000
+	nds.lhugp a0, target
+	nds.lhugp a0, 0x1000
+	nds.lwgp a0, target
+	nds.lwgp a0, 0x1000
+	nds.lwugp a0, target
+	nds.lwugp a0, 0x1000
+	nds.ldgp a0, target
+	nds.ldgp a0, 0x1000
+	nds.sbgp a0, target
+	nds.sbgp a0, 0x1000
+	nds.shgp a0, target
+	nds.shgp a0, 0x1000
+	nds.swgp a0, target
+	nds.swgp a0, 0x1000
+	nds.sdgp a0, target
+	nds.sdgp a0, 0x1000
+	nds.ffb a0, a1, a2
+	nds.ffzmism a0, a1, a2
+	nds.ffmism a0, a1, a2
+	nds.flmism a0, a1, a2
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index 8ce9fc98c43..7af96da42c0 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -99,6 +99,19 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type)
   RELOC_NUMBER (R_RISCV_VENDOR, 191)
 END_RELOC_NUMBERS ()
 
+/* Vendor (Andes) relocation types.  */
+START_RELOC_NUMBERS (elf_riscv_nds_reloc_type)
+  RELOC_NUMBER (R_RISCV_NDS_BRANCH_10, 241)
+  RELOC_NUMBER (R_RISCV_NDS_GPREL_18S0_I, 246)
+  RELOC_NUMBER (R_RISCV_NDS_GPREL_17S1_I, 247)
+  RELOC_NUMBER (R_RISCV_NDS_GPREL_17S2_I, 248)
+  RELOC_NUMBER (R_RISCV_NDS_GPREL_17S3_I, 249)
+  RELOC_NUMBER (R_RISCV_NDS_GPREL_18S0_S, 250)
+  RELOC_NUMBER (R_RISCV_NDS_GPREL_17S1_S, 251)
+  RELOC_NUMBER (R_RISCV_NDS_GPREL_17S2_S, 252)
+  RELOC_NUMBER (R_RISCV_NDS_GPREL_17S3_S, 253)
+END_RELOC_NUMBERS ()
+
 /* Internal relocations used exclusively by the relaxation pass.  */
 #define R_RISCV_DELETE  (R_RISCV_internal_first)
 #define R_RISCV_RVC_LUI (R_RISCV_internal_first + 1)
@@ -112,13 +125,17 @@ enum elf_riscv_vendor_id
   RISCV_VENDOR_ID_NONE = -1,
   /* Register vendor id here. The order must match the order of strings in the
      vendor string table.  */
+  RISCV_VENDOR_ID_NDS,
   RISCV_VENDOR_ID_COUNT
 };
 
+#define RISCV_VENDOR_STR_NDS "ANDES "
+
 #define RISCV_VENDOR_STR_TABLE_INITIALIZER				\
 {									\
   /* Register vendor strings (defined as macros) here. Remember to	\
      append a space to each string to avoid symbol conflicts.  */	\
+  RISCV_VENDOR_STR_NDS,							\
   NULL									\
 }
 
@@ -132,6 +149,7 @@ static const char *(*elf_riscv_vendor_reloc_type_funcs[]) (unsigned long) =
   /* Register vendor-defined functions (given a relocation number, return a
      relocation name string) here. The order must match the order of strings
      in the vendor string table.  */
+  elf_riscv_nds_reloc_type,
   NULL
 };
 
diff --git a/include/opcode/riscv-opc.h b/include/opcode/riscv-opc.h
index 1c649628390..85f579a5213 100644
--- a/include/opcode/riscv-opc.h
+++ b/include/opcode/riscv-opc.h
@@ -2520,6 +2520,65 @@
 #define MASK_VFWMACCBF16_VF 0xfc00707f
 #define MATCH_VFWMACCBF16_VV 0xec001057
 #define MASK_VFWMACCBF16_VV 0xfc00707f
+/* Vendor-specific (Andes) XAndesPerf instructions.  */
+#define MATCH_NDS_BBC 0x705b
+#define MASK_NDS_BBC 0x4000707f
+#define MATCH_NDS_BBS 0x4000705b
+#define MASK_NDS_BBS 0x4000707f
+#define MATCH_NDS_BEQC 0x505b
+#define MASK_NDS_BEQC 0x707f
+#define MATCH_NDS_BNEC 0x605b
+#define MASK_NDS_BNEC 0x707f
+#define MATCH_NDS_BFOS 0x305b
+#define MASK_NDS_BFOS 0x707f
+#define MATCH_NDS_BFOZ 0x205b
+#define MASK_NDS_BFOZ 0x707f
+#define MATCH_NDS_LEA_H 0xa00005b
+#define MASK_NDS_LEA_H 0xfe00707f
+#define MATCH_NDS_LEA_W 0xc00005b
+#define MASK_NDS_LEA_W 0xfe00707f
+#define MATCH_NDS_LEA_D 0xe00005b
+#define MASK_NDS_LEA_D 0xfe00707f
+#define MATCH_NDS_LEA_B_ZE 0x1000005b
+#define MASK_NDS_LEA_B_ZE 0xfe00707f
+#define MATCH_NDS_LEA_H_ZE 0x1200005b
+#define MASK_NDS_LEA_H_ZE 0xfe00707f
+#define MATCH_NDS_LEA_W_ZE 0x1400005b
+#define MASK_NDS_LEA_W_ZE 0xfe00707f
+#define MATCH_NDS_LEA_D_ZE 0x1600005b
+#define MASK_NDS_LEA_D_ZE 0xfe00707f
+#define MATCH_NDS_ADDIGP 0x100b
+#define MASK_NDS_ADDIGP 0x307f
+#define MATCH_NDS_LBGP 0xb
+#define MASK_NDS_LBGP 0x307f
+#define MATCH_NDS_LBUGP 0x200b
+#define MASK_NDS_LBUGP 0x307f
+#define MATCH_NDS_LHGP 0x102b
+#define MASK_NDS_LHGP 0x707f
+#define MATCH_NDS_LHUGP 0x502b
+#define MASK_NDS_LHUGP 0x707f
+#define MATCH_NDS_LWGP 0x202b
+#define MASK_NDS_LWGP 0x707f
+#define MATCH_NDS_LWUGP 0x602b
+#define MASK_NDS_LWUGP 0x707f
+#define MATCH_NDS_LDGP 0x302b
+#define MASK_NDS_LDGP 0x707f
+#define MATCH_NDS_SBGP 0x300b
+#define MASK_NDS_SBGP 0x307f
+#define MATCH_NDS_SHGP 0x2b
+#define MASK_NDS_SHGP 0x707f
+#define MATCH_NDS_SWGP 0x402b
+#define MASK_NDS_SWGP 0x707f
+#define MATCH_NDS_SDGP 0x702b
+#define MASK_NDS_SDGP 0x707f
+#define MATCH_NDS_FFB 0x2000005b
+#define MASK_NDS_FFB 0xfe00707f
+#define MATCH_NDS_FFZMISM 0x2200005b
+#define MASK_NDS_FFZMISM 0xfe00707f
+#define MATCH_NDS_FFMISM 0x2400005b
+#define MASK_NDS_FFMISM 0xfe00707f
+#define MATCH_NDS_FLMISM 0x2600005b
+#define MASK_NDS_FLMISM 0xfe00707f
 /* Vendor-specific (CORE-V) Xcvmac instructions.  */
 #define MATCH_CV_MAC       0x9000302b
 #define MASK_CV_MAC        0xfe00707f
@@ -4836,6 +4895,36 @@ DECLARE_INSN(cm_jalt, MATCH_CM_JALT, MASK_CM_JALT)
 DECLARE_INSN(sctrclr, MATCH_SCTRCLR, MASK_SCTRCLR)
 /* Smrnmi instruction */
 DECLARE_INSN(mnret, MATCH_MNRET, MASK_MNRET)
+/* Vendor-specific (Andes) XAndesPerf instructions.  */
+DECLARE_INSN(nds_bbc, MATCH_NDS_BBC, MASK_NDS_BBC)
+DECLARE_INSN(nds_bbs, MATCH_NDS_BBS, MASK_NDS_BBS)
+DECLARE_INSN(nds_beqc, MATCH_NDS_BEQC, MASK_NDS_BEQC)
+DECLARE_INSN(nds_bnec, MATCH_NDS_BNEC, MASK_NDS_BNEC)
+DECLARE_INSN(nds_bfos, MATCH_NDS_BFOS, MASK_NDS_BFOS)
+DECLARE_INSN(nds_bfoz, MATCH_NDS_BFOZ, MASK_NDS_BFOZ)
+DECLARE_INSN(nds_lea_h, MATCH_NDS_LEA_H, MASK_NDS_LEA_H)
+DECLARE_INSN(nds_lea_w, MATCH_NDS_LEA_W, MASK_NDS_LEA_W)
+DECLARE_INSN(nds_lea_d, MATCH_NDS_LEA_D, MASK_NDS_LEA_D)
+DECLARE_INSN(nds_lea_b_ze, MATCH_NDS_LEA_B_ZE, MASK_NDS_LEA_B_ZE)
+DECLARE_INSN(nds_lea_h_ze, MATCH_NDS_LEA_H_ZE, MASK_NDS_LEA_H_ZE)
+DECLARE_INSN(nds_lea_w_ze, MATCH_NDS_LEA_W_ZE, MASK_NDS_LEA_W_ZE)
+DECLARE_INSN(nds_lea_d_ze, MATCH_NDS_LEA_D_ZE, MASK_NDS_LEA_D_ZE)
+DECLARE_INSN(nds_addigp, MATCH_NDS_ADDIGP, MASK_NDS_ADDIGP)
+DECLARE_INSN(nds_lbgp, MATCH_NDS_LBGP, MASK_NDS_LBGP)
+DECLARE_INSN(nds_lbugp, MATCH_NDS_LBUGP, MASK_NDS_LBUGP)
+DECLARE_INSN(nds_lhgp, MATCH_NDS_LHGP, MASK_NDS_LHGP)
+DECLARE_INSN(nds_lhugp, MATCH_NDS_LHUGP, MASK_NDS_LHUGP)
+DECLARE_INSN(nds_lwgp, MATCH_NDS_LWGP, MASK_NDS_LWGP)
+DECLARE_INSN(nds_lwugp, MATCH_NDS_LWUGP, MASK_NDS_LWUGP)
+DECLARE_INSN(nds_ldgp, MATCH_NDS_LDGP, MASK_NDS_LDGP)
+DECLARE_INSN(nds_sbgp, MATCH_NDS_SBGP, MASK_NDS_SBGP)
+DECLARE_INSN(nds_shgp, MATCH_NDS_SHGP, MASK_NDS_SHGP)
+DECLARE_INSN(nds_swgp, MATCH_NDS_SWGP, MASK_NDS_SWGP)
+DECLARE_INSN(nds_sdgp, MATCH_NDS_SDGP, MASK_NDS_SDGP)
+DECLARE_INSN(nds_ffb, MATCH_NDS_FFB, MASK_NDS_FFB)
+DECLARE_INSN(nds_ffzmism, MATCH_NDS_FFZMISM, MASK_NDS_FFZMISM)
+DECLARE_INSN(nds_ffmism, MATCH_NDS_FFMISM, MASK_NDS_FFMISM)
+DECLARE_INSN(nds_flmism, MATCH_NDS_FLMISM, MASK_NDS_FLMISM)
 /* Vendor-specific (T-Head) XTheadBa instructions.  */
 DECLARE_INSN(th_addsl, MATCH_TH_ADDSL, MASK_TH_ADDSL)
 /* Vendor-specific (T-Head) XTheadBb instructions.  */
diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
index 858fcce6871..62b4643e3d7 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -117,6 +117,33 @@ static inline unsigned int riscv_insn_length (insn_t insn)
   (RV_X(x, 2, 2) << 4)
 #define EXTRACT_ZCMT_INDEX(x) \
   (RV_X(x, 2, 8))
+/* Vendor-specific (Andes) extract macros.  */
+#define EXTRACT_NDS_BRANCH_IMM6(x) \
+  (RV_X(x, 20, 5) | (RV_X(x, 7, 1) << 5))
+#define EXTRACT_NDS_BRANCH_IMM7(x) \
+  (RV_X(x, 20, 5) | (RV_X(x, 7, 1) << 5) | (RV_X(x, 30, 1) << 6))
+#define EXTRACT_NDS_BRANCH_IMM10(x) \
+  ((RV_X(x, 8, 4) << 1) | (RV_X(x, 25, 5) << 5) | (RV_IMM_SIGN(x) << 10))
+#define EXTRACT_NDS_BFO_IMM6H(x) \
+  (RV_X(x, 26, 6))
+#define EXTRACT_NDS_BFO_IMM6L(x) \
+  (RV_X(x, 20, 6))
+#define EXTRACT_NDS_LBGP_IMM(x) \
+  (RV_X(x, 14, 1) | (RV_X(x, 21, 10) << 1) | (RV_X(x, 20, 1) << 11) | (RV_X(x, 17, 3) << 12) | (RV_X(x, 15, 2) << 15) | (RV_IMM_SIGN(x) << 17))
+#define EXTRACT_NDS_LHGP_IMM(x) \
+  ((RV_X(x, 21, 10) << 1) | (RV_X(x, 20, 1) << 11) | (RV_X(x, 17, 3) << 12) | (RV_X(x, 15, 2) << 15) | (RV_IMM_SIGN(x) << 17))
+#define EXTRACT_NDS_LWGP_IMM(x) \
+  ((RV_X(x, 22, 9) << 2) | (RV_X(x, 20, 1) << 11) | (RV_X(x, 17, 3) << 12) | (RV_X(x, 15, 2) << 15) | (RV_X(x, 21, 1) << 17) | (RV_IMM_SIGN(x) << 18))
+#define EXTRACT_NDS_LDGP_IMM(x) \
+  ((RV_X(x, 23, 8) << 3) | (RV_X(x, 20, 1) << 11) | (RV_X(x, 17, 3) << 12) | (RV_X(x, 15, 2) << 15) | (RV_X(x, 21, 2) << 17) | (RV_IMM_SIGN(x) << 19))
+#define EXTRACT_NDS_SBGP_IMM(x) \
+  ((RV_X(x, 14, 1)) | (RV_X(x, 8, 4) << 1) | (RV_X(x, 25, 6) << 5) | (RV_X(x, 7, 1) << 11) | (RV_X(x, 17, 3) << 12) | (RV_X(x, 15, 2) << 15) | (RV_IMM_SIGN(x) << 17))
+#define EXTRACT_NDS_SHGP_IMM(x) \
+  ((RV_X(x, 8, 4) << 1) | (RV_X(x, 25, 6) << 5) | (RV_X(x, 7, 1) << 11) | (RV_X(x, 17, 3) << 12) | (RV_X(x, 15, 2) << 15) | (RV_IMM_SIGN(x) << 17))
+#define EXTRACT_NDS_SWGP_IMM(x) \
+  ((RV_X(x, 9, 3) << 2) | (RV_X(x, 25, 6) << 5) | (RV_X(x, 7, 1) << 11) | (RV_X(x, 17, 3) << 12) | (RV_X(x, 15, 2) << 15) | (RV_X(x, 8, 1) << 17) | (RV_IMM_SIGN(x) << 18))
+#define EXTRACT_NDS_SDGP_IMM(x) \
+  ((RV_X(x, 10, 2) << 3) | (RV_X(x, 25, 6) << 5) | (RV_X(x, 7, 1) << 11) | (RV_X(x, 17, 3) << 12) | (RV_X(x, 15, 2) << 15) | (RV_X(x, 8, 2) << 17) | (RV_IMM_SIGN(x) << 19))
 /* Vendor-specific (CORE-V) extract macros.  */
 #define EXTRACT_CV_IS2_UIMM5(x) \
   (RV_X(x, 20, 5))
@@ -196,6 +223,33 @@ static inline unsigned int riscv_insn_length (insn_t insn)
   (RV_X(x, 4, 2) << 2)
 #define ENCODE_ZCMT_INDEX(x) \
   (RV_X(x, 0, 8) << 2)
+/* Vendor-specific (Andes) encode macros.  */
+#define ENCODE_NDS_BRANCH_IMM6(x) \
+  ((RV_X(x, 0, 5) << 20) | (RV_X(x, 5, 1) << 7))
+#define ENCODE_NDS_BRANCH_IMM7(x) \
+  ((RV_X(x, 0, 5) << 20) | (RV_X(x, 5, 1) << 7) | (RV_X(x, 6, 1) << 30))
+#define ENCODE_NDS_BRANCH_IMM10(x) \
+  ((RV_X(x, 1, 4) << 8) | (RV_X(x, 5, 5) << 25) | (RV_X(x, 10, 1) << 31))
+#define ENCODE_NDS_BFO_IMM6H(x) \
+  (RV_X(x, 0, 6) << 26)
+#define ENCODE_NDS_BFO_IMM6L(x) \
+  (RV_X(x, 0, 6) << 20)
+#define ENCODE_NDS_LBGP_IMM(x) \
+  ((RV_X(x, 0, 1) << 14) | (RV_X(x, 1, 10) << 21) | (RV_X(x, 11, 1) << 20) | (RV_X(x, 12, 3) << 17) | (RV_X(x, 15, 2) << 15) | (RV_X(x, 17, 1) << 31))
+#define ENCODE_NDS_LHGP_IMM(x) \
+  ((RV_X(x, 1, 10) << 21) | (RV_X(x, 11, 1) << 20) | (RV_X(x, 12, 3) << 17) | (RV_X(x, 15, 2) << 15) | (RV_X(x, 17, 1) << 31))
+#define ENCODE_NDS_LWGP_IMM(x) \
+  ((RV_X(x, 2, 9) << 22) | (RV_X(x, 11, 1) << 20) | (RV_X(x, 12, 3) << 17) | (RV_X(x, 15, 2) << 15) | (RV_X(x, 17, 1) << 21) | (RV_X(x, 18, 1) << 31))
+#define ENCODE_NDS_LDGP_IMM(x) \
+  ((RV_X(x, 3, 8) << 23) | (RV_X(x, 11, 1) << 20) | (RV_X(x, 12, 3) << 17) | (RV_X(x, 15, 2) << 15) | (RV_X(x, 17, 2) << 21) | (RV_X(x, 19, 1) << 31))
+#define ENCODE_NDS_SBGP_IMM(x) \
+  ((RV_X(x, 0, 1) << 14) | (RV_X(x, 1, 4) << 8) | (RV_X(x, 5, 6) << 25) | (RV_X(x, 11, 1) << 7) | (RV_X(x, 12, 3) << 17) | (RV_X(x, 15, 2) << 15) | (RV_X(x, 17, 1) << 31))
+#define ENCODE_NDS_SHGP_IMM(x) \
+  ((RV_X(x, 1, 4) << 8) | (RV_X(x, 5, 6) << 25) | (RV_X(x, 11, 1) << 7) | (RV_X(x, 12, 3) << 17) | (RV_X(x, 15, 2) << 15) | (RV_X(x, 17, 1) << 31))
+#define ENCODE_NDS_SWGP_IMM(x) \
+  ((RV_X(x, 2, 3) << 9) | (RV_X(x, 5, 6) << 25) | (RV_X(x, 11, 1) << 7) | (RV_X(x, 12, 3) << 17) | (RV_X(x, 15, 2) << 15) | (RV_X(x, 17, 1) << 8) | (RV_X(x, 18, 1) << 31))
+#define ENCODE_NDS_SDGP_IMM(x) \
+  ((RV_X(x, 3, 2) << 10) | (RV_X(x, 5, 6) << 25) | (RV_X(x, 11, 1) << 7) | (RV_X(x, 12, 3) << 17) | (RV_X(x, 15, 2) << 15) | (RV_X(x, 17, 2) << 8) | (RV_X(x, 19, 1) << 31))
 /* Vendor-specific (CORE-V) encode macros.  */
 #define ENCODE_CV_IS2_UIMM5(x) \
   (RV_X(x, 0, 5) << 20)
@@ -246,6 +300,16 @@ static inline unsigned int riscv_insn_length (insn_t insn)
 #define VALID_ZCB_BYTE_UIMM(x) (EXTRACT_ZCB_BYTE_UIMM(ENCODE_ZCB_BYTE_UIMM(x)) == (x))
 #define VALID_ZCB_HALFWORD_UIMM(x) (EXTRACT_ZCB_HALFWORD_UIMM(ENCODE_ZCB_HALFWORD_UIMM(x)) == (x))
 #define VALID_ZCMP_SPIMM(x) (EXTRACT_ZCMP_SPIMM(ENCODE_ZCMP_SPIMM(x)) == (x))
+/* Vendor-specific (Andes) valid macros.  */
+#define VALID_NDS_BRANCH_IMM10(x) (EXTRACT_NDS_BRANCH_IMM10(ENCODE_NDS_BRANCH_IMM10(x)) == (x))
+#define VALID_NDS_LBGP_IMM(x) (EXTRACT_NDS_LBGP_IMM(ENCODE_NDS_LBGP_IMM(x)) == (x))
+#define VALID_NDS_LHGP_IMM(x) (EXTRACT_NDS_LHGP_IMM(ENCODE_NDS_LHGP_IMM(x)) == (x))
+#define VALID_NDS_LWGP_IMM(x) (EXTRACT_NDS_LWGP_IMM(ENCODE_NDS_LWGP_IMM(x)) == (x))
+#define VALID_NDS_LDGP_IMM(x) (EXTRACT_NDS_LDGP_IMM(ENCODE_NDS_LDGP_IMM(x)) == (x))
+#define VALID_NDS_SBGP_IMM(x) (EXTRACT_NDS_SBGP_IMM(ENCODE_NDS_SBGP_IMM(x)) == (x))
+#define VALID_NDS_SHGP_IMM(x) (EXTRACT_NDS_SHGP_IMM(ENCODE_NDS_SHGP_IMM(x)) == (x))
+#define VALID_NDS_SWGP_IMM(x) (EXTRACT_NDS_SWGP_IMM(ENCODE_NDS_SWGP_IMM(x)) == (x))
+#define VALID_NDS_SDGP_IMM(x) (EXTRACT_NDS_SDGP_IMM(ENCODE_NDS_SDGP_IMM(x)) == (x))
 
 #define RISCV_RTYPE(insn, rd, rs1, rs2) \
   ((MATCH_ ## insn) | ((rd) << OP_SH_RD) | ((rs1) << OP_SH_RS1) | ((rs2) << OP_SH_RS2))
@@ -284,6 +348,19 @@ static inline unsigned int riscv_insn_length (insn_t insn)
 #define RISCV_BRANCH_ALIGN (1 << RISCV_BRANCH_ALIGN_BITS)
 #define RISCV_BRANCH_REACH (RISCV_IMM_REACH * RISCV_BRANCH_ALIGN)
 
+/* Vendor-specific (Andes) immediate fields.  */
+#define RISCV_NDS_IMM7_BITS 7
+#define RISCV_NDS_IMM7_REACH (1LL << RISCV_NDS_IMM7_BITS)
+#define RISCV_NDS_IMM10_BITS 10
+#define RISCV_NDS_IMM10_REACH (1LL << RISCV_NDS_IMM10_BITS)
+#define RISCV_NDS_BRANCH_10_REACH (RISCV_NDS_IMM10_REACH * RISCV_BRANCH_ALIGN)
+#define RISCV_NDS_IMM18_BITS 18
+#define RISCV_NDS_IMM18_REACH (1LL << RISCV_NDS_IMM18_BITS)
+#define RISCV_NDS_IMM19_BITS 19
+#define RISCV_NDS_IMM19_REACH (1LL << RISCV_NDS_IMM19_BITS)
+#define RISCV_NDS_IMM20_BITS 20
+#define RISCV_NDS_IMM20_REACH (1LL << RISCV_NDS_IMM20_BITS)
+
 /* RV fields.  */
 
 #define OP_MASK_OP		0x7f
@@ -574,6 +651,7 @@ enum riscv_insn_class
   INSN_CLASS_ZACAS,
   INSN_CLASS_ZABHA_AND_ZACAS,
   INSN_CLASS_H,
+  INSN_CLASS_XANDESPERF,
   INSN_CLASS_XCVALU,
   INSN_CLASS_XCVBI,
   INSN_CLASS_XCVBITMANIP,
diff --git a/ld/emultempl/riscvelf.em b/ld/emultempl/riscvelf.em
index 78e1fcde68e..3e7884e9e21 100644
--- a/ld/emultempl/riscvelf.em
+++ b/ld/emultempl/riscvelf.em
@@ -81,7 +81,7 @@ riscv_elf_before_allocation (void)
 	ENABLE_RELAXATION;
     }
 
-  link_info.relax_pass = 2;
+  link_info.relax_pass = 4;
 }
 
 static void
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index b0d510ac8da..b7c6926848d 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -179,6 +179,9 @@ if [istarget "riscv*-*-*"] {
 	    "-march=rv64i -mabi=lp64" {weakref64.s} \
 	    {{objdump -d weakref64.d}} "weakref64"]]
 
+    run_dump_test "x-andes-perf-reloc"
+    run_dump_test "x-andes-perf-relax"
+
     # The following tests require shared library support.
     if ![check_shared_lib_support] {
 	return
diff --git a/ld/testsuite/ld-riscv-elf/x-andes-perf-relax.d b/ld/testsuite/ld-riscv-elf/x-andes-perf-relax.d
new file mode 100644
index 00000000000..0dc3bba963a
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/x-andes-perf-relax.d
@@ -0,0 +1,14 @@
+#source: x-andes-perf-relax.s
+#as: -march=rv64i_xandesperf
+#ld: -m[riscv_choose_lp64_emul] -Tx-andes-perf-relax.ld
+#objdump: -d
+
+[^:]*: *file format elf64-.*riscv
+
+Disassembly of section \.text:
+
+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+nds.lbgp[ 	]+.*[ 	]+<foo>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.lhgp[ 	]+.*[ 	]+<foo>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.swgp[ 	]+.*[ 	]+<foo>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.sdgp[ 	]+.*[ 	]+<foo>
diff --git a/ld/testsuite/ld-riscv-elf/x-andes-perf-relax.ld b/ld/testsuite/ld-riscv-elf/x-andes-perf-relax.ld
new file mode 100644
index 00000000000..bf94120b00c
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/x-andes-perf-relax.ld
@@ -0,0 +1,11 @@
+ENTRY(_start)
+SECTIONS
+{
+	.text 0x10000 : {
+		*(.text)
+	}
+	.data : {
+		__global_pointer$ = ALIGN(8);
+		*(.data)
+	}
+}
diff --git a/ld/testsuite/ld-riscv-elf/x-andes-perf-relax.s b/ld/testsuite/ld-riscv-elf/x-andes-perf-relax.s
new file mode 100644
index 00000000000..b6272d88306
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/x-andes-perf-relax.s
@@ -0,0 +1,23 @@
+	.text
+	.globl _start
+	.type _start,@function
+_start:
+	lui a0, %hi(foo)
+	lb a0, %lo(foo)(a0)
+
+.L1:
+	auipc a0, %pcrel_hi(foo)
+	lh a0, %pcrel_lo(.L1)(a0)
+
+	lui a0, %hi(foo)
+	sw a0, %lo(foo)(a0)
+
+.L2:
+	auipc a0, %pcrel_hi(foo)
+	sd a0, %pcrel_lo(.L2)(a0)
+
+	.data
+	.align 3
+	.zero 0x2000
+foo:
+	.dword 0x0
diff --git a/ld/testsuite/ld-riscv-elf/x-andes-perf-reloc.d b/ld/testsuite/ld-riscv-elf/x-andes-perf-reloc.d
new file mode 100644
index 00000000000..226381e67d3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/x-andes-perf-reloc.d
@@ -0,0 +1,26 @@
+#source: x-andes-perf-reloc.s
+#as: -march=rv64i_xandesperf
+#ld: -m[riscv_choose_lp64_emul]
+#objdump: -d
+
+[^:]*: *file format elf64-.*riscv
+
+Disassembly of section \.text:
+
+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+nds.bbc[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.bbs[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.beqc[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.bnec[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.addigp[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.lbgp[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.lbugp[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.lhgp[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.lhugp[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.lwgp[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.lwugp[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.ldgp[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.sbgp[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.shgp[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.swgp[ 	]+.*[ 	]+<_start>
+.*:[ 	]+[0-9a-f]+[ 	]+nds.sdgp[ 	]+.*[ 	]+<_start>
diff --git a/ld/testsuite/ld-riscv-elf/x-andes-perf-reloc.s b/ld/testsuite/ld-riscv-elf/x-andes-perf-reloc.s
new file mode 100644
index 00000000000..7172ee5a186
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/x-andes-perf-reloc.s
@@ -0,0 +1,20 @@
+	.text
+	.globl _start
+	.type _start,@function
+_start:
+	nds.bbc a0, 0, _start
+	nds.bbs a0, 0, _start
+	nds.beqc a0, 0, _start
+	nds.bnec a0, 0, _start
+	nds.addigp a0, _start
+	nds.lbgp a0, _start
+	nds.lbugp a0, _start
+	nds.lhgp a0, _start
+	nds.lhugp a0, _start
+	nds.lwgp a0, _start
+	nds.lwugp a0, _start
+	nds.ldgp a0, _start
+	nds.sbgp a0, _start
+	nds.shgp a0, _start
+	nds.swgp a0, _start
+	nds.sdgp a0, _start
diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c
index fa2d44af28f..45a686ec998 100644
--- a/opcodes/riscv-dis.c
+++ b/opcodes/riscv-dis.c
@@ -760,6 +760,95 @@ print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
 	case 'X': /* Vendor-specific operands.  */
 	  switch (*++oparg)
 	    {
+	    case 'a': /* Vendor-specific (Andes) operands.  */
+	      switch (*++oparg)
+		{
+		case 'g':
+		  info->target = EXTRACT_NDS_BRANCH_IMM10 (l) + pc;
+		  (*info->print_address_func) (info->target, info);
+		  break;
+		case 'h':
+		  print (info->stream, dis_style_immediate, "%d",
+			 (int)EXTRACT_NDS_BFO_IMM6H (l));
+		  break;
+		case 'i':
+		  print (info->stream, dis_style_immediate, "%d",
+			 (int)EXTRACT_NDS_BRANCH_IMM7 (l));
+		  break;
+		case 'k':
+		  print (info->stream, dis_style_immediate, "%d",
+			 (int)EXTRACT_NDS_BRANCH_IMM6 (l));
+		  break;
+		case 'l':
+		  print (info->stream, dis_style_immediate, "%d",
+			 (int)EXTRACT_NDS_BFO_IMM6L (l));
+		  break;
+		case 'G':
+		  switch (*++oparg)
+		    {
+		    case 'b':
+		      maybe_print_address (pd, X_GP, EXTRACT_NDS_LBGP_IMM (l),
+					   0);
+		      print (info->stream, dis_style_immediate, "%d",
+			     (int)EXTRACT_NDS_LBGP_IMM (l));
+		      break;
+		    case 'h':
+		      maybe_print_address (pd, X_GP, EXTRACT_NDS_LHGP_IMM (l),
+					   0);
+		      print (info->stream, dis_style_immediate, "%d",
+			     (int)EXTRACT_NDS_LHGP_IMM (l));
+		      break;
+		    case 'w':
+		      maybe_print_address (pd, X_GP, EXTRACT_NDS_LWGP_IMM (l),
+					   0);
+		      print (info->stream, dis_style_immediate, "%d",
+			     (int)EXTRACT_NDS_LWGP_IMM (l));
+		      break;
+		    case 'd':
+		      maybe_print_address (pd, X_GP, EXTRACT_NDS_LDGP_IMM (l),
+					   0);
+		      print (info->stream, dis_style_immediate, "%d",
+			     (int)EXTRACT_NDS_LDGP_IMM (l));
+		      break;
+		    default:
+		      goto undefined_modifier;
+		    }
+		  break;
+		case 'H':
+		  switch (*++oparg)
+		    {
+		    case 'b':
+		      maybe_print_address (pd, X_GP, EXTRACT_NDS_SBGP_IMM (l),
+					   0);
+		      print (info->stream, dis_style_immediate, "%d",
+			     (int)EXTRACT_NDS_SBGP_IMM (l));
+		      break;
+		    case 'h':
+		      maybe_print_address (pd, X_GP, EXTRACT_NDS_SHGP_IMM (l),
+					   0);
+		      print (info->stream, dis_style_immediate, "%d",
+			     (int)EXTRACT_NDS_SHGP_IMM (l));
+		      break;
+		    case 'w':
+		      maybe_print_address (pd, X_GP, EXTRACT_NDS_SWGP_IMM (l),
+					   0);
+		      print (info->stream, dis_style_immediate, "%d",
+			     (int)EXTRACT_NDS_SWGP_IMM (l));
+		      break;
+		    case 'd':
+		      maybe_print_address (pd, X_GP, EXTRACT_NDS_SDGP_IMM (l),
+					   0);
+		      print (info->stream, dis_style_immediate, "%d",
+			     (int)EXTRACT_NDS_SDGP_IMM (l));
+		      break;
+		    default:
+		      goto undefined_modifier;
+		    }
+		  break;
+		default:
+		  goto undefined_modifier;
+		}
+	      break;
 	    case 't': /* Vendor-specific (T-head) operands.  */
 	      {
 		size_t n;
diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
index e6fe5e9afba..dd713f6fa1b 100644
--- a/opcodes/riscv-opc.c
+++ b/opcodes/riscv-opc.c
@@ -2378,6 +2378,37 @@ const struct riscv_opcode riscv_opcodes[] =
 {"hsv.w",       0, INSN_CLASS_H, "t,0(s)", MATCH_HSV_W, MASK_HSV_W, match_opcode, INSN_DREF|INSN_4_BYTE },
 {"hsv.d",      64, INSN_CLASS_H, "t,0(s)", MATCH_HSV_D, MASK_HSV_D, match_opcode, INSN_DREF|INSN_8_BYTE },
 
+/* Vendor-specific (Andes) XAndesPerf instructions.  */
+{"nds.bbc",      0, INSN_CLASS_XANDESPERF, "s,Xak,Xag",   MATCH_NDS_BBC, MASK_NDS_BBC, match_opcode, INSN_CONDBRANCH},
+{"nds.bbs",      0, INSN_CLASS_XANDESPERF, "s,Xak,Xag",   MATCH_NDS_BBS, MASK_NDS_BBS, match_opcode, INSN_CONDBRANCH},
+{"nds.beqc",     0, INSN_CLASS_XANDESPERF, "s,Xai,Xag",   MATCH_NDS_BEQC, MASK_NDS_BEQC, match_opcode, INSN_CONDBRANCH},
+{"nds.bnec",     0, INSN_CLASS_XANDESPERF, "s,Xai,Xag",   MATCH_NDS_BNEC, MASK_NDS_BNEC, match_opcode, INSN_CONDBRANCH},
+{"nds.bfos",     0, INSN_CLASS_XANDESPERF, "d,s,Xah,Xal", MATCH_NDS_BFOS, MASK_NDS_BFOS, match_opcode, 0},
+{"nds.bfoz",     0, INSN_CLASS_XANDESPERF, "d,s,Xah,Xal", MATCH_NDS_BFOZ, MASK_NDS_BFOZ, match_opcode, 0},
+{"nds.lea.h",    0, INSN_CLASS_XANDESPERF, "d,s,t",       MATCH_NDS_LEA_H, MASK_NDS_LEA_H, match_opcode, 0},
+{"nds.lea.w",    0, INSN_CLASS_XANDESPERF, "d,s,t",       MATCH_NDS_LEA_W, MASK_NDS_LEA_W, match_opcode, 0},
+{"nds.lea.d",    0, INSN_CLASS_XANDESPERF, "d,s,t",       MATCH_NDS_LEA_D, MASK_NDS_LEA_D, match_opcode, 0},
+{"nds.lea.b.ze", 0, INSN_CLASS_XANDESPERF, "d,s,t",       MATCH_NDS_LEA_B_ZE, MASK_NDS_LEA_B_ZE, match_opcode, 0},
+{"nds.lea.h.ze", 0, INSN_CLASS_XANDESPERF, "d,s,t",       MATCH_NDS_LEA_H_ZE, MASK_NDS_LEA_H_ZE, match_opcode, 0},
+{"nds.lea.w.ze", 0, INSN_CLASS_XANDESPERF, "d,s,t",       MATCH_NDS_LEA_W_ZE, MASK_NDS_LEA_W_ZE, match_opcode, 0},
+{"nds.lea.d.ze", 0, INSN_CLASS_XANDESPERF, "d,s,t",       MATCH_NDS_LEA_D_ZE, MASK_NDS_LEA_D_ZE, match_opcode, 0},
+{"nds.addigp",   0, INSN_CLASS_XANDESPERF, "d,XaGb",      MATCH_NDS_ADDIGP, MASK_NDS_ADDIGP, match_opcode, 0},
+{"nds.lbgp",     0, INSN_CLASS_XANDESPERF, "d,XaGb",      MATCH_NDS_LBGP, MASK_NDS_LBGP, match_opcode, INSN_DREF|INSN_1_BYTE},
+{"nds.lbugp",    0, INSN_CLASS_XANDESPERF, "d,XaGb",      MATCH_NDS_LBUGP, MASK_NDS_LBUGP, match_opcode, INSN_DREF|INSN_1_BYTE},
+{"nds.lhgp",     0, INSN_CLASS_XANDESPERF, "d,XaGh",      MATCH_NDS_LHGP, MASK_NDS_LHGP, match_opcode, INSN_DREF|INSN_2_BYTE},
+{"nds.lhugp",    0, INSN_CLASS_XANDESPERF, "d,XaGh",      MATCH_NDS_LHUGP, MASK_NDS_LHUGP, match_opcode, INSN_DREF|INSN_2_BYTE},
+{"nds.lwgp",     0, INSN_CLASS_XANDESPERF, "d,XaGw",      MATCH_NDS_LWGP, MASK_NDS_LWGP, match_opcode, INSN_DREF|INSN_4_BYTE},
+{"nds.lwugp",   64, INSN_CLASS_XANDESPERF, "d,XaGw",      MATCH_NDS_LWUGP, MASK_NDS_LWUGP, match_opcode, INSN_DREF|INSN_4_BYTE},
+{"nds.ldgp",    64, INSN_CLASS_XANDESPERF, "d,XaGd",      MATCH_NDS_LDGP, MASK_NDS_LDGP, match_opcode, INSN_DREF|INSN_8_BYTE},
+{"nds.sbgp",     0, INSN_CLASS_XANDESPERF, "t,XaHb",      MATCH_NDS_SBGP, MASK_NDS_SBGP, match_opcode, INSN_DREF|INSN_1_BYTE},
+{"nds.shgp",     0, INSN_CLASS_XANDESPERF, "t,XaHh",      MATCH_NDS_SHGP, MASK_NDS_SHGP, match_opcode, INSN_DREF|INSN_2_BYTE},
+{"nds.swgp",     0, INSN_CLASS_XANDESPERF, "t,XaHw",      MATCH_NDS_SWGP, MASK_NDS_SWGP, match_opcode, INSN_DREF|INSN_4_BYTE},
+{"nds.sdgp",    64, INSN_CLASS_XANDESPERF, "t,XaHd",      MATCH_NDS_SDGP, MASK_NDS_SDGP, match_opcode, INSN_DREF|INSN_8_BYTE},
+{"nds.ffb",      0, INSN_CLASS_XANDESPERF, "d,s,t",       MATCH_NDS_FFB, MASK_NDS_FFB, match_opcode, 0},
+{"nds.ffzmism",  0, INSN_CLASS_XANDESPERF, "d,s,t",       MATCH_NDS_FFZMISM, MASK_NDS_FFZMISM, match_opcode, 0},
+{"nds.ffmism",   0, INSN_CLASS_XANDESPERF, "d,s,t",       MATCH_NDS_FFMISM, MASK_NDS_FFMISM, match_opcode, 0},
+{"nds.flmism",   0, INSN_CLASS_XANDESPERF, "d,s,t",       MATCH_NDS_FLMISM, MASK_NDS_FLMISM, match_opcode, 0},
+
 /* Vendor-specific (CORE-V) Xcvmac instructions.  */
 {"cv.mac",      0, INSN_CLASS_XCVMAC, "d,s,t",     MATCH_CV_MAC,      MASK_CV_MAC, match_opcode, 0},
 {"cv.msu",      0, INSN_CLASS_XCVMAC, "d,s,t",     MATCH_CV_MSU,      MASK_CV_MSU, match_opcode, 0},
-- 
2.49.0



More information about the Binutils mailing list