RISC-V: Support MIPS extensions: xmipscbop, xmipscmov, xmipsexectl, and xmipslsp

Message ID PAVPR09MB64512B898FB136275DCD7F04F7E62@PAVPR09MB6451.eurprd09.prod.outlook.com
State New
Headers
Series RISC-V: Support MIPS extensions: xmipscbop, xmipscmov, xmipsexectl, and xmipslsp |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 fail Patch failed to apply

Commit Message

Jovan Dmitrovic Jan. 21, 2025, 1:28 p.m. UTC
  HTEC Proprietary

Hello everyone,
this patch includes support for various MIPS custom instructions available on the
new P8700 CPU, based on the RISC-V platform.

Spec for the P8700 CPU can be found on the following link:
https://mips.com/wp-content/uploads/2024/11/P8700-F_Programmers_Reference_Manual_Rev1.80_11-13-2024.pdf

Regards,
Jovan
  

Patch

From e26a5104014a4e63e1203775ef27410571310b8c Mon Sep 17 00:00:00 2001
From: Chao-ying Fu <cfu@wavecomp.com>
Date: Mon, 23 Dec 2024 16:32:14 -0800
Subject: [PATCH] RISC-V: Support MIPS extensions: xmipscbop, xmipscmov,
 xmipsexectl, and xmipslsp

MIPS custom instructions include pref, ccmov, ehb, ihb, mipspause, ldp, lwp,
sdp, and swp.

Spec: https://mips.com/wp-content/uploads/2024/11/P8700-F_Programmers_Reference_Manual_Rev1.80_11-13-2024.pdf .

Signed-off-by: Jovan Dmitrović <jovan.dmitrovic@htecgroup.com>
Signed-off-by: Chao-ying Fu <cfu@wavecomp.com>
---
 bfd/elfxx-riscv.c                    |  12 ++++
 gas/NEWS                             |   6 +-
 gas/config/tc-riscv.c                | 101 +++++++++++++++++++++++++++
 gas/testsuite/gas/riscv/march-help.l |   4 ++
 gas/testsuite/gas/riscv/mips-insns.d |  31 ++++++++
 gas/testsuite/gas/riscv/mips-insns.s |  42 +++++++++++
 include/opcode/riscv-opc.h           |  29 ++++++++
 include/opcode/riscv.h               |  40 +++++++++++
 opcodes/riscv-dis.c                  |  36 ++++++++++
 opcodes/riscv-opc.c                  |  12 ++++
 10 files changed, 311 insertions(+), 2 deletions(-)
 create mode 100644 gas/testsuite/gas/riscv/mips-insns.d
 create mode 100644 gas/testsuite/gas/riscv/mips-insns.s

diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index a609781bc04..47602514ed7 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -1504,6 +1504,10 @@  static struct riscv_supported_ext riscv_supported_vendor_x_ext[] =
   {"xsfvqmaccqoq",	ISA_SPEC_CLASS_DRAFT,	1, 0, 0},
   {"xsfvqmaccdod",	ISA_SPEC_CLASS_DRAFT,	1, 0, 0},
   {"xsfvfnrclipxfqf",	ISA_SPEC_CLASS_DRAFT,	1, 0, 0},
+  {"xmipscbop",		ISA_SPEC_CLASS_DRAFT,	1, 0, 0},
+  {"xmipscmov",		ISA_SPEC_CLASS_DRAFT,	1, 0, 0},
+  {"xmipsexectl",	ISA_SPEC_CLASS_DRAFT,	1, 0, 0},
+  {"xmipslsp",		ISA_SPEC_CLASS_DRAFT,	1, 0, 0},
   {NULL, 0, 0, 0, 0}
 };
 
@@ -2793,6 +2797,14 @@  riscv_multi_subset_supports (riscv_parse_subset_t *rps,
       return riscv_subset_supports (rps, "xsfvqmaccdod");
     case INSN_CLASS_XSFVFNRCLIPXFQF:
       return riscv_subset_supports (rps, "xsfvfnrclipxfqf");
+    case INSN_CLASS_XMIPSCBOP:
+      return riscv_subset_supports (rps, "xmipscbop");
+    case INSN_CLASS_XMIPSCMOV:
+      return riscv_subset_supports (rps, "xmipscmov");
+    case INSN_CLASS_XMIPSEXECTL:
+      return riscv_subset_supports (rps, "xmipsexectl");
+    case INSN_CLASS_XMIPSLSP:
+      return riscv_subset_supports (rps, "xmipslsp");
     default:
       rps->error_handler
         (_("internal: unreachable INSN_CLASS_*"));
diff --git a/gas/NEWS b/gas/NEWS
index ce5124da2bb..3b4d40e7e3e 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -24,8 +24,10 @@ 
 * On x86 emulation support (for secondary targets) was dropped.
 
 * Add support for RISC-V Zcmp (cm.mva01s, cm.mvsa01), Smrnmi, S[sm]dbltrp,
-  CORE-V (xcvbitmanip, xcvsimd) extensions with version 1.0 and more SiFive
-  extensions (xsfvqmaccdod, xsfvqmaccqoq and xsfvfnrclipxfqf).
+  CORE-V (xcvbitmanip, xcvsimd) extensions with version 1.0, more SiFive
+  extensions (xsfvqmaccdod, xsfvqmaccqoq and xsfvfnrclipxfqf, and MIPS
+  extensions (xmipscbop, xmipscmov, xmipsexectl, and xmipslsp): pref, ccmov,
+  ehb, ihb, mipspause, ldp, lwp, sdp, and swp.
 
 Changes in 2.43:
 
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index a915c8b4995..5d8bda6c719 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -1750,6 +1750,21 @@  validate_riscv_insn (const struct riscv_opcode *opc, int length)
 		    goto unknown_validate_operand;
 		}
 		break;
+	    case 'm': /* Vendor-specific (MIPS) operands.  */
+	      switch (*++oparg)
+		{
+		  case '@': USE_BITS (OP_MASK_MIPS_HINT, OP_SH_MIPS_HINT);
+			    break;
+		  case '#': USE_BITS (OP_MASK_MIPS_IMM9, OP_SH_MIPS_IMM9);
+			    break;
+		  case '$': used_bits |= ENCODE_MIPS_LDP_IMM (-1U); break;
+		  case '%': used_bits |= ENCODE_MIPS_LWP_IMM (-1U); break;
+		  case '^': used_bits |= ENCODE_MIPS_SDP_IMM (-1U); break;
+		  case '&': used_bits |= ENCODE_MIPS_SWP_IMM (-1U); break;
+		  default:
+		    goto unknown_validate_operand;
+		}
+		break;
 	    default:
 	      goto unknown_validate_operand;
 	    }
@@ -4173,6 +4188,92 @@  riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
 #undef ENCODE_UIMM_BIT_FIELD
 		  break;
 
+		case 'm': /* Vendor-specific (MIPS) operands.  */
+		  switch (*++oparg)
+		    {
+		    case '@': /* hint 0 - 31.  */
+		      my_getExpression (imm_expr, asarg);
+		      check_absolute_expr (ip, imm_expr, FALSE);
+		      if ((unsigned long)imm_expr->X_add_number > 31)
+			as_bad(_("Improper hint amount (%lu)"),
+			       (unsigned long)imm_expr->X_add_number);
+		      INSERT_OPERAND(MIPS_HINT, *ip, imm_expr->X_add_number);
+		      imm_expr->X_op = O_absent;
+		      asarg = expr_parse_end;
+		      continue;
+
+		    case '#': /* immediate 0 - 511.  */
+		      my_getExpression (imm_expr, asarg);
+		      check_absolute_expr (ip, imm_expr, FALSE);
+		      if ((unsigned long)imm_expr->X_add_number > 511)
+			as_bad(_("Improper immediate amount (%lu)"),
+			       (unsigned long)imm_expr->X_add_number);
+		      INSERT_OPERAND(MIPS_IMM9, *ip, imm_expr->X_add_number);
+		      imm_expr->X_op = O_absent;
+		      asarg = expr_parse_end;
+		      continue;
+
+		    case '$': /* LDP offset 0 to (1<<7)-8.  */
+		      my_getExpression (imm_expr, asarg);
+		      check_absolute_expr (ip, imm_expr, FALSE);
+		      if ((unsigned long)imm_expr->X_add_number >= (1 << 7)
+			  || ((unsigned long)imm_expr->X_add_number & 0x7) != 0)
+			as_bad(_("Improper LDP offset amount (%lu)"),
+			       (unsigned long)imm_expr->X_add_number);
+		      INSERT_OPERAND(MIPS_LDP_OFFSET, *ip,
+				     (imm_expr->X_add_number >> 3));
+		      imm_expr->X_op = O_absent;
+		      asarg = expr_parse_end;
+		      continue;
+
+		    case '%': /* LWP offset 0 to (1<<7)-4.  */
+		      my_getExpression (imm_expr, asarg);
+		      check_absolute_expr (ip, imm_expr, FALSE);
+		      if ((unsigned long)imm_expr->X_add_number >= (1 << 7)
+			  || ((unsigned long)imm_expr->X_add_number & 0x3) != 0)
+			as_bad(_("Improper LWP offset amount (%lu)"),
+			       (unsigned long)imm_expr->X_add_number);
+		      INSERT_OPERAND(MIPS_LWP_OFFSET, *ip,
+				     (imm_expr->X_add_number >> 2));
+		      imm_expr->X_op = O_absent;
+		      asarg = expr_parse_end;
+		      continue;
+
+		    case '^': /* SDP offset 0 to (1<<7)-8.  */
+		      my_getExpression (imm_expr, asarg);
+		      check_absolute_expr (ip, imm_expr, FALSE);
+		      if ((unsigned long)imm_expr->X_add_number >= (1 << 7)
+			  || ((unsigned long)imm_expr->X_add_number & 0x7) != 0)
+			as_bad(_("Improper SDP offset amount (%lu)"),
+			       (unsigned long)imm_expr->X_add_number);
+		      INSERT_OPERAND(MIPS_SDP_OFFSET10, *ip,
+				     (imm_expr->X_add_number >> 3));
+		      INSERT_OPERAND(MIPS_SDP_OFFSET25, *ip,
+				     (imm_expr->X_add_number >> 5));
+		      imm_expr->X_op = O_absent;
+		      asarg = expr_parse_end;
+		      continue;
+
+		    case '&': /* SWP offset 0 to (1<<7)-4.  */
+		      my_getExpression (imm_expr, asarg);
+		      check_absolute_expr (ip, imm_expr, FALSE);
+		      if ((unsigned long)imm_expr->X_add_number >= (1 << 7)
+			  || ((unsigned long)imm_expr->X_add_number & 0x3) != 0)
+			as_bad(_("Improper SWP offset amount (%lu)"),
+			       (unsigned long)imm_expr->X_add_number);
+		      INSERT_OPERAND(MIPS_SWP_OFFSET9, *ip,
+				     (imm_expr->X_add_number >> 2));
+		      INSERT_OPERAND(MIPS_SWP_OFFSET25, *ip,
+				     (imm_expr->X_add_number >> 5));
+		      imm_expr->X_op = O_absent;
+		      asarg = expr_parse_end;
+		      continue;
+
+		    default:
+		      goto unknown_riscv_ip_operand;
+		    }
+		  break;
+
 		default:
 		  goto unknown_riscv_ip_operand;
 		}
diff --git a/gas/testsuite/gas/riscv/march-help.l b/gas/testsuite/gas/riscv/march-help.l
index fd1174059e5..2f4e8e4306e 100644
--- a/gas/testsuite/gas/riscv/march-help.l
+++ b/gas/testsuite/gas/riscv/march-help.l
@@ -162,3 +162,7 @@  All available -march extensions for RISC-V:
 	xsfvqmaccqoq                            1.0
 	xsfvqmaccdod                            1.0
 	xsfvfnrclipxfqf                         1.0
+	xmipscbop                               1.0
+	xmipscmov                               1.0
+	xmipsexectl                             1.0
+	xmipslsp                                1.0
diff --git a/gas/testsuite/gas/riscv/mips-insns.d b/gas/testsuite/gas/riscv/mips-insns.d
new file mode 100644
index 00000000000..922be836ec5
--- /dev/null
+++ b/gas/testsuite/gas/riscv/mips-insns.d
@@ -0,0 +1,31 @@ 
+#as:
+#objdump: -dr
+
+.*:[   ]+file format .*
+
+
+Disassembly of section .text:
+
+0+000 <.text>:
+[ 	]+[0-9a-f]+:[ 	]+0003000b[ 	]+pref[ 	]+0x0,0x0\(t1\)
+[ 	]+[0-9a-f]+:[ 	]+1ff38f8b[ 	]+pref[ 	]+0x1f,0x1ff\(t2\)
+[ 	]+[0-9a-f]+:[ 	]+6eb6350b[ 	]+ccmov[ 	]+a0,a1,a2,a3
+[ 	]+[0-9a-f]+:[ 	]+00301013[ 	]+ehb
+[ 	]+[0-9a-f]+:[ 	]+00101013[ 	]+ihb
+[ 	]+[0-9a-f]+:[ 	]+00501013[ 	]+mipspause
+[ 	]+[0-9a-f]+:[ 	]+e80f4e0b[ 	]+ldp[ 	]+t3,t4,0\(t5\)
+[ 	]+[0-9a-f]+:[ 	]+e88f4e0b[ 	]+ldp[ 	]+t3,t4,8\(t5\)
+[ 	]+[0-9a-f]+:[ 	]+1f00cf8b[ 	]+ldp[ 	]+t6,gp,112\(ra\)
+[ 	]+[0-9a-f]+:[ 	]+1f80cf8b[ 	]+ldp[ 	]+t6,gp,120\(ra\)
+[ 	]+[0-9a-f]+:[ 	]+5816450b[ 	]+lwp[ 	]+a0,a1,0\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+5856450b[ 	]+lwp[ 	]+a0,a1,4\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+7797c68b[ 	]+lwp[ 	]+a3,a4,120\(a5\)
+[ 	]+[0-9a-f]+:[ 	]+77d7c68b[ 	]+lwp[ 	]+a3,a4,124\(a5\)
+[ 	]+[0-9a-f]+:[ 	]+e9cf500b[ 	]+sdp[ 	]+t3,t4,0\(t5\)
+[ 	]+[0-9a-f]+:[ 	]+e9cf540b[ 	]+sdp[ 	]+t3,t4,8\(t5\)
+[ 	]+[0-9a-f]+:[ 	]+1ff0d80b[ 	]+sdp[ 	]+t6,gp,112\(ra\)
+[ 	]+[0-9a-f]+:[ 	]+1ff0dc0b[ 	]+sdp[ 	]+t6,gp,120\(ra\)
+[ 	]+[0-9a-f]+:[ 	]+58a6508b[ 	]+swp[ 	]+a0,a1,0\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+58a6528b[ 	]+swp[ 	]+a0,a1,4\(a2\)
+[ 	]+[0-9a-f]+:[ 	]+76d7dc8b[ 	]+swp[ 	]+a3,a4,120\(a5\)
+[ 	]+[0-9a-f]+:[ 	]+76d7de8b[ 	]+swp[ 	]+a3,a4,124\(a5\)
diff --git a/gas/testsuite/gas/riscv/mips-insns.s b/gas/testsuite/gas/riscv/mips-insns.s
new file mode 100644
index 00000000000..820be76ff8c
--- /dev/null
+++ b/gas/testsuite/gas/riscv/mips-insns.s
@@ -0,0 +1,42 @@ 
+	.attribute arch, "rv64i"
+	# xmipscbop
+	.option push
+	.option arch, +xmipscbop
+	pref	0, 0(t1)
+	pref	31, 511(t2)
+	.option pop
+
+	# xmipscmov
+	.option push
+	.option arch, +xmipscmov
+	ccmov	a0,a1,a2,a3
+	.option pop
+
+	# xmipsexectl
+	.option push
+	.option arch, +xmipsexectl
+	ehb
+	ihb
+	mipspause
+	.option pop
+
+	# xmipslsp
+	.option push
+	.option arch, +xmipslsp
+	ldp	t3, t4, 0(t5)
+	ldp	t3, t4, 8(t5)
+	ldp	t6, gp, 112(ra)
+	ldp	t6, gp, 120(ra)
+	lwp	a0, a1, 0(a2)
+	lwp	a0, a1, 4(a2)
+	lwp	a3, a4, 120(a5)
+	lwp	a3, a4, 124(a5)
+	sdp	t3, t4, 0(t5)
+	sdp	t3, t4, 8(t5)
+	sdp	t6, gp, 112(ra)
+	sdp	t6, gp, 120(ra)
+	swp	a0, a1, 0(a2)
+	swp	a0, a1, 4(a2)
+	swp	a3, a4, 120(a5)
+	swp	a3, a4, 124(a5)
+	.option pop
diff --git a/include/opcode/riscv-opc.h b/include/opcode/riscv-opc.h
index 166424226aa..63be0693fbb 100644
--- a/include/opcode/riscv-opc.h
+++ b/include/opcode/riscv-opc.h
@@ -3767,6 +3767,25 @@ 
 #define MASK_SFVFNRCLIPXUFQF 0xfe00707f
 #define MATCH_SFVFNRCLIPXFQF 0x8e00505b
 #define MASK_SFVFNRCLIPXFQF 0xfe00707f
+/* MIPS custom instruction.  */
+#define MATCH_MIPS_CCMOV 0x600300b
+#define MASK_MIPS_CCMOV 0x600707f
+#define MATCH_MIPS_LWP 0x0010400b
+#define MASK_MIPS_LWP  0x0030707f
+#define MATCH_MIPS_LDP 0x0000400b
+#define MASK_MIPS_LDP  0x0070707f
+#define MATCH_MIPS_SWP 0x0000508b
+#define MASK_MIPS_SWP  0x000071ff
+#define MATCH_MIPS_SDP 0x0000500b
+#define MASK_MIPS_SDP  0x000073ff
+#define MATCH_MIPS_EHB 0x00301013
+#define MASK_MIPS_EHB  0xffffffff
+#define MATCH_MIPS_IHB 0x00101013
+#define MASK_MIPS_IHB  0xffffffff
+#define MATCH_MIPS_PAUSE 0x00501013
+#define MASK_MIPS_PAUSE  0xffffffff
+#define MATCH_MIPS_PREF 0x0000000b
+#define MASK_MIPS_PREF 0xe000707f
 /* Unprivileged Counter/Timers CSR addresses.  */
 #define CSR_CYCLE 0xc00
 #define CSR_TIME 0xc01
@@ -4884,6 +4903,16 @@  DECLARE_INSN(th_sync_s, MATCH_TH_SYNC_S, MASK_TH_SYNC_S)
 /* XVentanaCondOps instructions. */
 DECLARE_INSN(vt_maskc, MATCH_VT_MASKC, MASK_VT_MASKC)
 DECLARE_INSN(vt_maskcn, MATCH_VT_MASKCN, MASK_VT_MASKCN)
+/* MIPS custom instructions.  */
+DECLARE_INSN(ccmov, MATCH_MIPS_CCMOV, MASK_MIPS_CCMOV)
+DECLARE_INSN(lwp, MATCH_MIPS_LWP, MASK_MIPS_LWP)
+DECLARE_INSN(ldp, MATCH_MIPS_LDP, MASK_MIPS_LDP)
+DECLARE_INSN(swp, MATCH_MIPS_SWP, MASK_MIPS_SWP)
+DECLARE_INSN(sdp, MATCH_MIPS_SDP, MASK_MIPS_SDP)
+DECLARE_INSN(ehb, MATCH_MIPS_EHB, MASK_MIPS_EHB)
+DECLARE_INSN(ihb, MATCH_MIPS_IHB, MASK_MIPS_IHB)
+DECLARE_INSN(mipspause, MATCH_MIPS_PAUSE, MASK_MIPS_PAUSE)
+DECLARE_INSN(pref, MATCH_MIPS_PREF, MASK_MIPS_PREF)
 #endif /* DECLARE_INSN */
 #ifdef DECLARE_CSR
 /* Unprivileged Counter/Timers CSRs.  */
diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
index de4c13fb6db..9b60ee66263 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -132,6 +132,15 @@  static inline unsigned int riscv_insn_length (insn_t insn)
   ((RV_X(x, 25, 1)) | (RV_X(x, 20, 5) << 1) | (RV_IMM_SIGN_N(x, 20, 5) << 5))
 #define EXTRACT_CV_SIMD_UIMM6(x) \
   ((RV_X(x, 25, 1)) | (RV_X(x, 20, 5) << 1))
+/* Vendor-specific (MIPS) extract macros.  */
+#define EXTRACT_MIPS_LWP_IMM(x) \
+  (RV_X(x, 22, 5) << 2)
+#define EXTRACT_MIPS_LDP_IMM(x) \
+  (RV_X(x, 23, 4) << 3)
+#define EXTRACT_MIPS_SWP_IMM(x) \
+  ((RV_X(x, 25, 2) << 5) | (RV_X(x, 9, 3) << 2))
+#define EXTRACT_MIPS_SDP_IMM(x) \
+  ((RV_X(x, 25, 2) << 5) | (RV_X(x, 10, 2) << 3))
 
 #define ENCODE_ITYPE_IMM(x) \
   (RV_X(x, 0, 12) << 20)
@@ -200,6 +209,15 @@  static inline unsigned int riscv_insn_length (insn_t insn)
   ((RV_X(x, 0, 1) << 25) | (RV_X(x, 1, 5) << 20))
 #define ENCODE_CV_SIMD_UIMM6(x) \
   ((RV_X(x, 0, 1) << 25) | (RV_X(x, 1, 5) << 20))
+/* Vendor-specific (MIPS) encode macros.  */
+#define ENCODE_MIPS_LWP_IMM(x) \
+  (RV_X(x, 2, 5) << 22)
+#define ENCODE_MIPS_LDP_IMM(x) \
+  (RV_X(x, 3, 4) << 23)
+#define ENCODE_MIPS_SWP_IMM(x) \
+  ((RV_X(x, 5, 2) << 25) | (RV_X(x, 2, 3) << 9))
+#define ENCODE_MIPS_SDP_IMM(x) \
+  ((RV_X(x, 5, 2) << 25) | (RV_X(x, 3, 2) << 10))
 
 #define VALID_ITYPE_IMM(x) (EXTRACT_ITYPE_IMM(ENCODE_ITYPE_IMM(x)) == (x))
 #define VALID_STYPE_IMM(x) (EXTRACT_STYPE_IMM(ENCODE_STYPE_IMM(x)) == (x))
@@ -383,6 +401,24 @@  static inline unsigned int riscv_insn_length (insn_t insn)
 #define OP_MASK_XSO1            0x1
 #define OP_SH_XSO1              26
 
+/* MIPS fields.  */
+#define OP_MASK_MIPS_IMM9		0x1ff
+#define OP_SH_MIPS_IMM9		20
+#define OP_MASK_MIPS_HINT		0x1f
+#define OP_SH_MIPS_HINT		7
+#define OP_MASK_MIPS_LWP_OFFSET     0x1f
+#define OP_SH_MIPS_LWP_OFFSET       22
+#define OP_MASK_MIPS_LDP_OFFSET     0xf
+#define OP_SH_MIPS_LDP_OFFSET       23
+#define OP_MASK_MIPS_SWP_OFFSET9    0x7
+#define OP_SH_MIPS_SWP_OFFSET9      9
+#define OP_MASK_MIPS_SWP_OFFSET25   0x3
+#define OP_SH_MIPS_SWP_OFFSET25     25
+#define OP_MASK_MIPS_SDP_OFFSET10   0x3
+#define OP_SH_MIPS_SDP_OFFSET10     10
+#define OP_MASK_MIPS_SDP_OFFSET25   0x3
+#define OP_SH_MIPS_SDP_OFFSET25     25
+
 /* ABI names for selected x-registers.  */
 
 #define X_RA 1
@@ -557,6 +593,10 @@  enum riscv_insn_class
   INSN_CLASS_XSFVQMACCQOQ,
   INSN_CLASS_XSFVQMACCDOD,
   INSN_CLASS_XSFVFNRCLIPXFQF,
+  INSN_CLASS_XMIPSCBOP,
+  INSN_CLASS_XMIPSCMOV,
+  INSN_CLASS_XMIPSEXECTL,
+  INSN_CLASS_XMIPSLSP,
 };
 
 /* This structure holds information for a particular instruction.  */
diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c
index 367004d3341..e8e40b0f869 100644
--- a/opcodes/riscv-dis.c
+++ b/opcodes/riscv-dis.c
@@ -517,6 +517,11 @@  print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
 	    print (info->stream, dis_style_immediate, "0");
 	  break;
 
+	case 'r':
+	  print (info->stream, dis_style_register, "%s",
+		 riscv_gpr_names[EXTRACT_OPERAND (RS3, l)]);
+	  break;
+
 	case 's':
 	  if ((l & MASK_JALR) == MATCH_JALR)
 	    maybe_print_address (pd, rs1, EXTRACT_ITYPE_IMM (l), 0);
@@ -880,6 +885,37 @@  print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
 		  break;
 		}
 	      break;
+	    case 'm': /* Vendor-specific (MIPS) operands.  */
+	      switch (*++oparg)
+		{
+		case '@':
+		  print (info->stream, dis_style_register, "0x%x",
+			 (unsigned) EXTRACT_OPERAND (MIPS_HINT, l));
+		  break;
+		case '#':
+		  print (info->stream, dis_style_register, "0x%x",
+			 (unsigned) EXTRACT_OPERAND (MIPS_IMM9, l));
+		  break;
+		case '$':
+		  print (info->stream, dis_style_immediate, "%d",
+			 (unsigned)EXTRACT_MIPS_LDP_IMM (l));
+		  break;
+		case '%':
+		  print (info->stream, dis_style_immediate, "%d",
+			 (unsigned)EXTRACT_MIPS_LWP_IMM (l));
+		  break;
+		case '^':
+		  print (info->stream, dis_style_immediate, "%d",
+			 (unsigned)EXTRACT_MIPS_SDP_IMM (l));
+		  break;
+		case '&':
+		  print (info->stream, dis_style_immediate, "%d",
+			 (unsigned)EXTRACT_MIPS_SWP_IMM (l));
+		  break;
+		default:
+		  goto undefined_modifier;
+		}
+	      break;
 	    default:
 	      goto undefined_modifier;
 	    }
diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
index 5680f6f96d6..9c64354fd45 100644
--- a/opcodes/riscv-opc.c
+++ b/opcodes/riscv-opc.c
@@ -507,6 +507,10 @@  const struct riscv_opcode riscv_opcodes[] =
 {"la.tls.gd",   0, INSN_CLASS_I, "d,A",       0, (int) M_LA_TLS_GD, NULL, INSN_MACRO },
 {"la.tls.ie",   0, INSN_CLASS_I, "d,A",       0, (int) M_LA_TLS_IE, match_rd_nonzero, INSN_MACRO },
 {"neg",         0, INSN_CLASS_I, "d,t",       MATCH_SUB, MASK_SUB|MASK_RS1, match_opcode, INSN_ALIAS }, /* sub 0  */
+/* Put MIPS custom instructions: ehb, ihb, and mipspause before slli.  */
+{"ehb", 0, INSN_CLASS_XMIPSEXECTL, "", MATCH_MIPS_EHB, MASK_MIPS_EHB, match_opcode, 0 },
+{"ihb", 0, INSN_CLASS_XMIPSEXECTL, "", MATCH_MIPS_IHB, MASK_MIPS_IHB, match_opcode, 0 },
+{"mipspause", 0, INSN_CLASS_XMIPSEXECTL, "", MATCH_MIPS_PAUSE, MASK_MIPS_PAUSE, match_opcode, 0 },
 {"slli",        0, INSN_CLASS_C, "d,CU,C>",   MATCH_C_SLLI, MASK_C_SLLI, match_slli_as_c_slli, INSN_ALIAS },
 {"slli",        0, INSN_CLASS_I, "d,s,>",     MATCH_SLLI, MASK_SLLI, match_opcode, 0 },
 {"sll",         0, INSN_CLASS_C, "d,CU,C>",   MATCH_C_SLLI, MASK_C_SLLI, match_slli_as_c_slli, INSN_ALIAS },
@@ -3463,6 +3467,14 @@  const struct riscv_opcode riscv_opcodes[] =
 {"sf.vfnrclip.xu.f.qf", 0, INSN_CLASS_XSFVFNRCLIPXFQF, "Vd,Vt,S", MATCH_SFVFNRCLIPXUFQF, MASK_SFVFNRCLIPXUFQF, match_opcode, 0},
 {"sf.vfnrclip.x.f.qf",  0, INSN_CLASS_XSFVFNRCLIPXFQF, "Vd,Vt,S", MATCH_SFVFNRCLIPXFQF, MASK_SFVFNRCLIPXFQF, match_opcode, 0},
 
+/* MIPS custom instructions.  */
+{"ccmov", 0, INSN_CLASS_XMIPSCMOV, "d,t,s,r", MATCH_MIPS_CCMOV, MASK_MIPS_CCMOV, match_opcode, 0},
+{"ldp", 0, INSN_CLASS_XMIPSLSP, "d,r,Xm$(s)", MATCH_MIPS_LDP, MASK_MIPS_LDP, match_opcode, 0 },
+{"lwp", 0, INSN_CLASS_XMIPSLSP, "d,r,Xm%(s)", MATCH_MIPS_LWP, MASK_MIPS_LWP, match_opcode, 0 },
+{"pref", 0, INSN_CLASS_XMIPSCBOP, "Xm@,Xm#(s)", MATCH_MIPS_PREF, MASK_MIPS_PREF, match_opcode, 0 },
+{"sdp", 0, INSN_CLASS_XMIPSLSP, "t,r,Xm^(s)", MATCH_MIPS_SDP, MASK_MIPS_SDP, match_opcode, 0 },
+{"swp", 0, INSN_CLASS_XMIPSLSP, "t,r,Xm&(s)", MATCH_MIPS_SWP, MASK_MIPS_SWP, match_opcode, 0 },
+
 /* Terminate the list.  */
 {0, 0, INSN_CLASS_NONE, 0, 0, 0, 0, 0}
 };
-- 
2.34.1