public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v3 1/2] RISC-V: Support Zcmp push/pop instructions.
@ 2023-11-20  7:06 Jiawei
  2023-11-20  7:06 ` [PATCH v3 2/2] RISC-V: Support Zcmp cm.mv instructions Jiawei
  2024-01-05  7:01 ` [PATCH v3 1/2] RISC-V: Support Zcmp push/pop instructions Kito Cheng
  0 siblings, 2 replies; 4+ messages in thread
From: Jiawei @ 2023-11-20  7:06 UTC (permalink / raw)
  To: binutils
  Cc: nelson, kito.cheng, palmer, jbeulich, research_trasio,
	christoph.muellner, jeremy.bennett, nandni.jamnadas,
	mary.bennett, charlie.keaney, simon.cook, sinan.lin, gaofei,
	fujin.zhao, wuwei2016, shihua, shiyulong, chenyixuan, Jiawei

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 35052 bytes --]

Support zcmp extension push/pop/popret and popret zero instructions.
`reg_list` is a list containing 1 to 13 registers, we can use: "{ra}, {ra,
s0}, {ra, s0-s1}, {ra, s0-s2}, …, {ra, s0-sN}" to present this feature.
`stack_adj` is the total size of the stack frame, use
`riscv_get_sp_base` function to calculate it.
Most work was finished by Sinan Lin.

Co-Authored by: Charlie Keaney <charlie.keaney@embecosm.com>
Co-Authored by: Mary Bennett <mary.bennett@embecosm.com>
Co-Authored by: Nandni Jamnadas <nandni.jamnadas@embecosm.com>
Co-Authored by: Sinan Lin <sinan.lin@linux.alibaba.com>
Co-Authored by: Simon Cook <simon.cook@embecosm.com>
Co-Authored by: Shihua Liao <shihua@iscas.ac.cn>
Co-Authored by: Yulong Shi <yulong@iscas.ac.cn>

Version log:

Fixes format issues, rename some functions, use 'strtok' 
in reglist parser, adds conflict check with 'Zcd' extension.

bfd/ChangeLog:

        * elfxx-riscv.c (riscv_parse_check_conflicts): New function.
        (riscv_multi_subset_supports): New extension.
        (riscv_multi_subset_supports_ext): Ditto.
        * elfxx-riscv.h (SP_ALIGNMENT): New macro.

gas/ChangeLog:

        * config/tc-riscv.c (regno_to_rlist): New function.
        (reglist_lookup): Ditto.
        (validate_riscv_insn): New operators.
        (riscv_ip): Ditto.
        * testsuite/gas/riscv/zcmp-push-pop-fail.d: New test.
        * testsuite/gas/riscv/zcmp-push-pop-fail.l: New test.
        * testsuite/gas/riscv/zcmp-push-pop-fail.s: New test.
        * testsuite/gas/riscv/zcmp-push-pop.d: New test.
        * testsuite/gas/riscv/zcmp-push-pop.s: New test.

include/ChangeLog:

        * opcode/riscv-opc.h (MATCH_CM_PUSH): New opcode.
        (MASK_CM_PUSH): New mask.
        (MATCH_CM_POP): New opcode.
        (MASK_CM_POP): New mask.
        (MATCH_CM_POPRET): New opcode.
        (MASK_CM_POPRET): New mask.
        (MATCH_CM_POPRETZ): New opcode.
        (MASK_CM_POPRETZ): New mask.
        (DECLARE_INSN): New declarations.
        * opcode/riscv.h (EXTRACT_ZCMP_SPIMM): New inline function.
        (ENCODE_ZCMP_SPIMM): Ditto.
        (VALID_ZCMP_SPIMM): Ditto.
        (OP_MASK_RLIST): New mask.
        (OP_SH_RLIST): New operand code.
        (SP_ALIGNMENT): New argument.
        (X_S0): New reg number.
        (X_S1): Ditto.
        (X_S2): Ditto.
        (X_S10): Ditto.
        (X_S11): Ditto.
        (enum riscv_insn_class): New extension class.
        (riscv_get_sp_base): New function.

opcodes/ChangeLog:

        * riscv-dis.c (set_default_riscv_dis_options): New var.
        (parse_riscv_dis_option_without_args): Ditto.
        (print_rlist): New print function.
        (riscv_get_spimm): New function.
        (print_insn_args): New operators.
        * riscv-opc.c: New instructions.

---
 bfd/elfxx-riscv.c                            |  13 ++
 bfd/elfxx-riscv.h                            |   1 +
 gas/config/tc-riscv.c                        | 148 +++++++++++++++++
 gas/testsuite/gas/riscv/zcmp-push-pop-fail.d |   3 +
 gas/testsuite/gas/riscv/zcmp-push-pop-fail.l |   9 ++
 gas/testsuite/gas/riscv/zcmp-push-pop-fail.s |  13 ++
 gas/testsuite/gas/riscv/zcmp-push-pop.d      | 154 ++++++++++++++++++
 gas/testsuite/gas/riscv/zcmp-push-pop.s      | 162 +++++++++++++++++++
 include/opcode/riscv-opc.h                   |  14 ++
 include/opcode/riscv.h                       |  30 ++++
 opcodes/riscv-dis.c                          |  57 +++++++
 opcodes/riscv-opc.c                          |   6 +
 12 files changed, 610 insertions(+)
 create mode 100644 gas/testsuite/gas/riscv/zcmp-push-pop-fail.d
 create mode 100644 gas/testsuite/gas/riscv/zcmp-push-pop-fail.l
 create mode 100644 gas/testsuite/gas/riscv/zcmp-push-pop-fail.s
 create mode 100644 gas/testsuite/gas/riscv/zcmp-push-pop.d
 create mode 100644 gas/testsuite/gas/riscv/zcmp-push-pop.s

diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index c9acf124626..f5728e0b4ba 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -1190,6 +1190,7 @@ static struct riscv_implicit_subset riscv_implicit_subsets[] =
   {"zcf", "zca",	check_implicit_always},
   {"zcd", "zca",	check_implicit_always},
   {"zcb", "zca",	check_implicit_always},
+  {"zcmp", "zca",	check_implicit_always},
   {"smaia", "ssaia",		check_implicit_always},
   {"smcntrpmf", "zicsr",	check_implicit_always},
   {"smstateen", "ssstateen",	check_implicit_always},
@@ -1332,6 +1333,7 @@ static struct riscv_supported_ext riscv_supported_std_z_ext[] =
   {"zcb",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 },
   {"zcf",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 },
   {"zcd",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 },
+  {"zcmp",		ISA_SPEC_CLASS_DRAFT,		1, 0,  0 },
   {NULL, 0, 0, 0, 0}
 };
 
@@ -1970,6 +1972,13 @@ riscv_parse_check_conflicts (riscv_parse_subset_t *rps)
       rps->error_handler (_("rv%d does not support the `q' extension"), xlen);
       no_conflict = false;
     }
+  if (riscv_subset_supports (rps, "zcmp")
+      && riscv_subset_supports (rps, "zcd"))
+    {
+      rps->error_handler
+	(_("zcmp' is incompatible with `d/zcd' extension"));
+      no_conflict = false;
+    }
   if (riscv_lookup_subset (rps->subset_list, "zcf", &subset)
       && xlen > 32)
     {
@@ -2548,6 +2557,8 @@ riscv_multi_subset_supports (riscv_parse_subset_t *rps,
     case INSN_CLASS_ZCB_AND_ZMMUL:
       return (riscv_subset_supports (rps, "zcb")
 	      && riscv_subset_supports (rps, "zmmul"));
+    case INSN_CLASS_ZCMP:
+      return riscv_subset_supports (rps, "zcmp");
     case INSN_CLASS_SVINVAL:
       return riscv_subset_supports (rps, "svinval");
     case INSN_CLASS_H:
@@ -2792,6 +2803,8 @@ riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
       return _("zcb' and `zbb");
     case INSN_CLASS_ZCB_AND_ZMMUL:
       return _("zcb' and `zmmul', or `zcb' and `m");
+    case INSN_CLASS_ZCMP:
+      return "zcmp";
     case INSN_CLASS_SVINVAL:
       return "svinval";
     case INSN_CLASS_H:
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index abcb409bd78..6f551bb6907 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -26,6 +26,7 @@
 #include "cpu-riscv.h"
 
 #define RISCV_UNKNOWN_VERSION -1
+#define SP_ALIGNMENT 16
 
 struct riscv_elf_params
 {
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 402c46ad753..a46de00fc32 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -1246,6 +1246,125 @@ flt_lookup (float f, const float *array, size_t size, unsigned *regnop)
   return false;
 }
 
+/* Map ra and s-register to [4,15], so that we can check if the
+   reg2 in register list reg1-reg2 or single reg2 is valid or not,
+   and obtain the corresponding rlist value.
+
+   ra - 4
+   s0 - 5
+   s1 - 6
+    ....
+   s10 - 0 (invalid)
+   s11 - 15.  */
+
+static int
+regno_to_rlist (unsigned regno)
+{
+  if (regno == X_RA)
+    return 4;
+  else if (regno == X_S0 || regno == X_S1)
+    return 5 + regno - X_S0;
+  else if (regno >= X_S2 && regno < X_S10)
+    return 7 + regno - X_S2;
+  else if (regno == X_S11)
+    return 15;
+
+  return 0; /* invalid symbol */
+}
+
+/* Parse register list, and the parsed rlist value is stored in rlist
+   argument.
+
+   If ABI register names are used (e.g. ra and s0), the register
+   list could be "{ra}", "{ra, s0}", "{ra, s0-sN}", where 0 < N < 10 or
+   N == 11.
+
+   If numeric register names are used (e.g. x1 and x8), the register list
+   could be "{x1}", "{x1,x8}", "{x1,x8-x9}", "{x1,x8-x9,x18}" and
+   "{x1,x8-x9,x18-xN}", where 19 < N < 25 or N == 27.
+
+   It will fail if numeric register names and ABI register names are used
+   at the same time.
+*/
+
+static bool
+reglist_lookup (char **s, unsigned *rlist)
+{
+  unsigned regno = 0;
+  unsigned regnum = 0;
+  char *reglist = strdup(*s);
+  char *regname[3];
+
+  if (reglist == NULL)
+    return false;
+
+  reglist = strtok(reglist, "}");
+  for(reglist = strtok(reglist, ",");reglist;reglist = strtok(NULL, ",")){
+    regname[regnum] = reglist;
+    regnum++;
+  }
+
+  /* Use to check if the register format is xreg.  */
+  bool use_xreg = **s == 'x';
+
+  /* The first register in register list should be ra.  */
+  if (!reg_lookup (s, RCLASS_GPR, &regno)
+     || !(*rlist = regno_to_rlist (regno)) /* update rlist */
+     || regno != X_RA)
+    return false;
+
+  if (regnum == 1)
+    return true;
+
+  /* Do not use numeric and abi names at the same time.  */
+  if ((*++*s != 'x') && use_xreg)
+    return false;
+  /* Reg1 should be s0 or its numeric names x8.  */
+  if (!reg_lookup (s, RCLASS_GPR, &regno)
+     || !(*rlist = regno_to_rlist (regno))
+     || regno != X_S0)
+    return false;
+
+  if(strlen(regname[1]) == 2)
+    return true;
+
+  if ((*++*s != 'x') && use_xreg)
+    return false;
+  /* Reg2 is x9 if the numeric name is used, otherwise,
+    it could be any other sN register, where N > 0.  */
+  if (!reg_lookup (s, RCLASS_GPR, &regno)
+     || !(*rlist = regno_to_rlist (regno))
+     || regno <= X_S0
+     || (use_xreg && regno != X_S1))
+    return false;
+
+  if (regnum == 2)
+    return true;
+
+  if(regnum == 3 && use_xreg){
+    if ((*++*s != 'x') && use_xreg)
+      return false;
+    /* Reg3 should be s2.  */
+    if (!reg_lookup (s, RCLASS_GPR, &regno)
+       || !(*rlist = regno_to_rlist (regno))
+       || regno != X_S2)
+      return false;
+    if(strlen(regname[2]) == 3)
+      return true;
+    if ((*++*s != 'x') && use_xreg)
+      return false;
+    /* Reg4 could be any other sN register, where N > 1.  */
+    if (!reg_lookup (s, RCLASS_GPR, &regno)
+       || !(*rlist = regno_to_rlist (regno))
+       || regno <= X_S2)
+      return false;
+    return true;
+  }
+
+  free(reglist);
+  return false;
+}
+
 #define USE_BITS(mask,shift) (used_bits |= ((insn_t)(mask) << (shift)))
 #define USE_IMM(n, s) \
   (used_bits |= ((insn_t)((1ull<<n)-1) << (s)))
@@ -1362,6 +1481,8 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
 	case ',': break;
 	case '(': break;
 	case ')': break;
+	case '{': break;
+	case '}': break;
 	case '<': USE_BITS (OP_MASK_SHAMTW, OP_SH_SHAMTW); break;
 	case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
 	case 'A': break; /* Macro operand, must be symbol.  */
@@ -1442,6 +1563,10 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
 		case 'h': used_bits |= ENCODE_ZCB_HALFWORD_UIMM (-1U); break;
 		/* halfword immediate operators, load/store halfword insns.  */
 		case 'b': used_bits |= ENCODE_ZCB_BYTE_UIMM (-1U); break;
+		/* immediate offset operand for cm.push and cm.pop.  */
+		case 'p': used_bits |= ENCODE_ZCMP_SPIMM (-1U); break;
+		/* register list operand for cm.push and cm.pop.  */
+		case 'r': USE_BITS (OP_MASK_RLIST, OP_SH_RLIST); break;
 		case 'f': break;
 		default:
 		  goto unknown_validate_operand;
@@ -3066,6 +3191,8 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
 	    case ')':
 	    case '[':
 	    case ']':
+	    case '{':
+	    case '}':
 	      if (*asarg++ == *oparg)
 		continue;
 	      break;
@@ -3521,6 +3648,27 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
 			break;
 		      ip->insn_opcode |= ENCODE_ZCB_BYTE_UIMM (imm_expr->X_add_number);
 		      goto rvc_imm_done;
+		    case 'r':
+		      /* we use regno to store reglist value here.  */
+		      if (!reglist_lookup (&asarg, &regno))
+			break;
+		      INSERT_OPERAND (RLIST, *ip, regno);
+			continue;
+		    case 'p':
+		      if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
+			|| imm_expr->X_op != O_constant)
+			break;
+		      /* convert stack adjust of cm.push to a positive offset.  */
+		      if (ip->insn_mo->match == MATCH_CM_PUSH)
+			imm_expr->X_add_number *= -1;
+		      /* subtract base stack adjust and get spimm.  */
+		      imm_expr->X_add_number -=
+			      riscv_get_sp_base (ip->insn_opcode, *riscv_rps_as.xlen);
+		      if (!VALID_ZCMP_SPIMM (imm_expr->X_add_number))
+			break;
+		      ip->insn_opcode |=
+			ENCODE_ZCMP_SPIMM (imm_expr->X_add_number);
+		      goto rvc_imm_done;
 		    case 'f': /* Operand for matching immediate 255.  */
 		      if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
 			  || imm_expr->X_op != O_constant
diff --git a/gas/testsuite/gas/riscv/zcmp-push-pop-fail.d b/gas/testsuite/gas/riscv/zcmp-push-pop-fail.d
new file mode 100644
index 00000000000..84ecf263c54
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmp-push-pop-fail.d
@@ -0,0 +1,3 @@
+#as: -march=rv64i_zcmp
+#source: zcmp-push-pop-fail.s
+#error_output: zcmp-push-pop-fail.l
diff --git a/gas/testsuite/gas/riscv/zcmp-push-pop-fail.l b/gas/testsuite/gas/riscv/zcmp-push-pop-fail.l
new file mode 100644
index 00000000000..955e495d5bb
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmp-push-pop-fail.l
@@ -0,0 +1,9 @@
+.*: Assembler messages:
+.*: Error: illegal operands `cm.push \{a0\},-64'
+.*: Error: illegal operands `cm.pop \{ra,s1\},-64'
+.*: Error: illegal operands `cm.popret \{ra,s2-s3\},-64'
+.*: Error: illegal operands `cm.popretz \{ra,s0-s10\},-112'
+.*: Error: illegal operands `cm.push \{ra\},0'
+.*: Error: illegal operands `cm.pop \{ra,s0\},-80'
+.*: Error: illegal operands `cm.popret \{ra,s0-s1\},-15'
+.*: Error: illegal operands `cm.popretz \{ra,s0-s11\},-165'
diff --git a/gas/testsuite/gas/riscv/zcmp-push-pop-fail.s b/gas/testsuite/gas/riscv/zcmp-push-pop-fail.s
new file mode 100644
index 00000000000..a82f9399787
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmp-push-pop-fail.s
@@ -0,0 +1,13 @@
+target:
+
+	# rlist
+	cm.push {a0}, -64
+	cm.pop {ra, s1}, -64
+	cm.popret {ra, s2-s3}, -64
+	cm.popretz {ra, s0-s10}, -112
+
+	# spimm
+	cm.push {ra}, 0
+	cm.pop {ra, s0}, -80
+	cm.popret {ra, s0-s1}, -15
+	cm.popretz {ra, s0-s11}, -165
diff --git a/gas/testsuite/gas/riscv/zcmp-push-pop.d b/gas/testsuite/gas/riscv/zcmp-push-pop.d
new file mode 100644
index 00000000000..e21295051ec
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmp-push-pop.d
@@ -0,0 +1,154 @@
+#as: -march=rv64i_zcmp
+#source: zcmp-push-pop.s
+#objdump: -dr -Mno-aliases
+
+.*:[	 ]+file format .*
+
+
+Disassembly of section .text:
+
+0+000 <target>:
+[	 ]*[0-9a-f]+:[	 ]+b84e[	 ]+cm.push[	 ]+\{ra\},-64
+[	 ]*[0-9a-f]+:[	 ]+b85e[	 ]+cm.push[	 ]+\{ra,s0\},-64
+[	 ]*[0-9a-f]+:[	 ]+b86a[	 ]+cm.push[	 ]+\{ra,s0-s1\},-64
+[	 ]*[0-9a-f]+:[	 ]+b87a[	 ]+cm.push[	 ]+\{ra,s0-s2\},-64
+[	 ]*[0-9a-f]+:[	 ]+b8da[	 ]+cm.push[	 ]+\{ra,s0-s8\},-112
+[	 ]*[0-9a-f]+:[	 ]+b8e6[	 ]+cm.push[	 ]+\{ra,s0-s9\},-112
+[	 ]*[0-9a-f]+:[	 ]+b8f2[	 ]+cm.push[	 ]+\{ra,s0-s11\},-112
+[	 ]*[0-9a-f]+:[	 ]+b84e[	 ]+cm.push[	 ]+\{ra\},-64
+[	 ]*[0-9a-f]+:[	 ]+b85e[	 ]+cm.push[	 ]+\{ra,s0\},-64
+[	 ]*[0-9a-f]+:[	 ]+b86a[	 ]+cm.push[	 ]+\{ra,s0-s1\},-64
+[	 ]*[0-9a-f]+:[	 ]+b87a[	 ]+cm.push[	 ]+\{ra,s0-s2\},-64
+[	 ]*[0-9a-f]+:[	 ]+b8da[	 ]+cm.push[	 ]+\{ra,s0-s8\},-112
+[	 ]*[0-9a-f]+:[	 ]+b8e6[	 ]+cm.push[	 ]+\{ra,s0-s9\},-112
+[	 ]*[0-9a-f]+:[	 ]+b8f2[	 ]+cm.push[	 ]+\{ra,s0-s11},-112
+[	 ]*[0-9a-f]+:[	 ]+b842[	 ]+cm.push[	 ]+\{ra\},-16
+[	 ]*[0-9a-f]+:[	 ]+b846[	 ]+cm.push[	 ]+\{ra\},-32
+[	 ]*[0-9a-f]+:[	 ]+b84e[	 ]+cm.push[	 ]+\{ra\},-64
+[	 ]*[0-9a-f]+:[	 ]+b872[	 ]+cm.push[	 ]+\{ra,s0-s2\},-32
+[	 ]*[0-9a-f]+:[	 ]+b87a[	 ]+cm.push[	 ]+\{ra,s0-s2\},-64
+[	 ]*[0-9a-f]+:[	 ]+b87e[	 ]+cm.push[	 ]+\{ra,s0-s2\},-80
+[	 ]*[0-9a-f]+:[	 ]+b882[	 ]+cm.push[	 ]+\{ra,s0-s3\},-48
+[	 ]*[0-9a-f]+:[	 ]+b886[	 ]+cm.push[	 ]+\{ra,s0-s3\},-64
+[	 ]*[0-9a-f]+:[	 ]+b88e[	 ]+cm.push[	 ]+\{ra,s0-s3\},-96
+[	 ]*[0-9a-f]+:[	 ]+b8b2[	 ]+cm.push[	 ]+\{ra,s0-s6\},-64
+[	 ]*[0-9a-f]+:[	 ]+b8b6[	 ]+cm.push[	 ]+\{ra,s0-s6\},-80
+[	 ]*[0-9a-f]+:[	 ]+b8be[	 ]+cm.push[	 ]+\{ra,s0-s6\},-112
+[	 ]*[0-9a-f]+:[	 ]+b8c2[	 ]+cm.push[	 ]+\{ra,s0-s7\},-80
+[	 ]*[0-9a-f]+:[	 ]+b8c6[	 ]+cm.push[	 ]+\{ra,s0-s7\},-96
+[	 ]*[0-9a-f]+:[	 ]+b8ce[	 ]+cm.push[	 ]+\{ra,s0-s7\},-128
+[	 ]*[0-9a-f]+:[	 ]+b8e2[	 ]+cm.push[	 ]+\{ra,s0-s9\},-96
+[	 ]*[0-9a-f]+:[	 ]+b8e6[	 ]+cm.push[	 ]+\{ra,s0-s9\},-112
+[	 ]*[0-9a-f]+:[	 ]+b8ee[	 ]+cm.push[	 ]+\{ra,s0-s9\},-144
+[	 ]*[0-9a-f]+:[	 ]+b8f2[	 ]+cm.push[	 ]+\{ra,s0-s11\},-112
+[	 ]*[0-9a-f]+:[	 ]+b8f6[	 ]+cm.push[	 ]+\{ra,s0-s11\},-128
+[	 ]*[0-9a-f]+:[	 ]+b8fa[	 ]+cm.push[	 ]+\{ra,s0-s11\},-144
+[	 ]*[0-9a-f]+:[	 ]+b8fe[	 ]+cm.push[	 ]+\{ra,s0-s11\},-160
+[	 ]*[0-9a-f]+:[	 ]+ba4e[	 ]+cm.pop[	 ]+\{ra\},64
+[	 ]*[0-9a-f]+:[	 ]+ba5e[	 ]+cm.pop[	 ]+\{ra,s0\},64
+[	 ]*[0-9a-f]+:[	 ]+ba6a[	 ]+cm.pop[	 ]+\{ra,s0-s1\},64
+[	 ]*[0-9a-f]+:[	 ]+ba7a[	 ]+cm.pop[	 ]+\{ra,s0-s2\},64
+[	 ]*[0-9a-f]+:[	 ]+bada[	 ]+cm.pop[	 ]+\{ra,s0-s8\},112
+[	 ]*[0-9a-f]+:[	 ]+bae6[	 ]+cm.pop[	 ]+\{ra,s0-s9\},112
+[	 ]*[0-9a-f]+:[	 ]+baf2[	 ]+cm.pop[	 ]+\{ra,s0-s11\},112
+[	 ]*[0-9a-f]+:[	 ]+ba4e[	 ]+cm.pop[	 ]+\{ra\},64
+[	 ]*[0-9a-f]+:[	 ]+ba5e[	 ]+cm.pop[	 ]+\{ra,s0\},64
+[	 ]*[0-9a-f]+:[	 ]+ba6a[	 ]+cm.pop[	 ]+\{ra,s0-s1\},64
+[	 ]*[0-9a-f]+:[	 ]+ba7a[	 ]+cm.pop[	 ]+\{ra,s0-s2\},64
+[	 ]*[0-9a-f]+:[	 ]+bada[	 ]+cm.pop[	 ]+\{ra,s0-s8\},112
+[	 ]*[0-9a-f]+:[	 ]+bae6[	 ]+cm.pop[	 ]+\{ra,s0-s9\},112
+[	 ]*[0-9a-f]+:[	 ]+baf2[	 ]+cm.pop[	 ]+\{ra,s0-s11},112
+[	 ]*[0-9a-f]+:[	 ]+ba42[	 ]+cm.pop[	 ]+\{ra\},16
+[	 ]*[0-9a-f]+:[	 ]+ba46[	 ]+cm.pop[	 ]+\{ra\},32
+[	 ]*[0-9a-f]+:[	 ]+ba4e[	 ]+cm.pop[	 ]+\{ra\},64
+[	 ]*[0-9a-f]+:[	 ]+ba72[	 ]+cm.pop[	 ]+\{ra,s0-s2\},32
+[	 ]*[0-9a-f]+:[	 ]+ba7a[	 ]+cm.pop[	 ]+\{ra,s0-s2\},64
+[	 ]*[0-9a-f]+:[	 ]+ba7e[	 ]+cm.pop[	 ]+\{ra,s0-s2\},80
+[	 ]*[0-9a-f]+:[	 ]+ba82[	 ]+cm.pop[	 ]+\{ra,s0-s3\},48
+[	 ]*[0-9a-f]+:[	 ]+ba86[	 ]+cm.pop[	 ]+\{ra,s0-s3\},64
+[	 ]*[0-9a-f]+:[	 ]+ba8e[	 ]+cm.pop[	 ]+\{ra,s0-s3\},96
+[	 ]*[0-9a-f]+:[	 ]+bab2[	 ]+cm.pop[	 ]+\{ra,s0-s6\},64
+[	 ]*[0-9a-f]+:[	 ]+bab6[	 ]+cm.pop[	 ]+\{ra,s0-s6\},80
+[	 ]*[0-9a-f]+:[	 ]+babe[	 ]+cm.pop[	 ]+\{ra,s0-s6\},112
+[	 ]*[0-9a-f]+:[	 ]+bac2[	 ]+cm.pop[	 ]+\{ra,s0-s7\},80
+[	 ]*[0-9a-f]+:[	 ]+bac6[	 ]+cm.pop[	 ]+\{ra,s0-s7\},96
+[	 ]*[0-9a-f]+:[	 ]+bace[	 ]+cm.pop[	 ]+\{ra,s0-s7\},128
+[	 ]*[0-9a-f]+:[	 ]+bae2[	 ]+cm.pop[	 ]+\{ra,s0-s9\},96
+[	 ]*[0-9a-f]+:[	 ]+bae6[	 ]+cm.pop[	 ]+\{ra,s0-s9\},112
+[	 ]*[0-9a-f]+:[	 ]+baee[	 ]+cm.pop[	 ]+\{ra,s0-s9\},144
+[	 ]*[0-9a-f]+:[	 ]+baf2[	 ]+cm.pop[	 ]+\{ra,s0-s11\},112
+[	 ]*[0-9a-f]+:[	 ]+baf6[	 ]+cm.pop[	 ]+\{ra,s0-s11\},128
+[	 ]*[0-9a-f]+:[	 ]+bafa[	 ]+cm.pop[	 ]+\{ra,s0-s11\},144
+[	 ]*[0-9a-f]+:[	 ]+bafe[	 ]+cm.pop[	 ]+\{ra,s0-s11\},160
+[	 ]*[0-9a-f]+:[	 ]+be4e[	 ]+cm.popret[	 ]+\{ra\},64
+[	 ]*[0-9a-f]+:[	 ]+be5e[	 ]+cm.popret[	 ]+\{ra,s0\},64
+[	 ]*[0-9a-f]+:[	 ]+be6a[	 ]+cm.popret[	 ]+\{ra,s0-s1\},64
+[	 ]*[0-9a-f]+:[	 ]+be7a[	 ]+cm.popret[	 ]+\{ra,s0-s2\},64
+[	 ]*[0-9a-f]+:[	 ]+beda[	 ]+cm.popret[	 ]+\{ra,s0-s8\},112
+[	 ]*[0-9a-f]+:[	 ]+bee6[	 ]+cm.popret[	 ]+\{ra,s0-s9\},112
+[	 ]*[0-9a-f]+:[	 ]+bef2[	 ]+cm.popret[	 ]+\{ra,s0-s11\},112
+[	 ]*[0-9a-f]+:[	 ]+be4e[	 ]+cm.popret[	 ]+\{ra\},64
+[	 ]*[0-9a-f]+:[	 ]+be5e[	 ]+cm.popret[	 ]+\{ra,s0\},64
+[	 ]*[0-9a-f]+:[	 ]+be6a[	 ]+cm.popret[	 ]+\{ra,s0-s1\},64
+[	 ]*[0-9a-f]+:[	 ]+be7a[	 ]+cm.popret[	 ]+\{ra,s0-s2\},64
+[	 ]*[0-9a-f]+:[	 ]+beda[	 ]+cm.popret[	 ]+\{ra,s0-s8\},112
+[	 ]*[0-9a-f]+:[	 ]+bee6[	 ]+cm.popret[	 ]+\{ra,s0-s9\},112
+[	 ]*[0-9a-f]+:[	 ]+bef2[	 ]+cm.popret[	 ]+\{ra,s0-s11},112
+[	 ]*[0-9a-f]+:[	 ]+be42[	 ]+cm.popret[	 ]+\{ra\},16
+[	 ]*[0-9a-f]+:[	 ]+be46[	 ]+cm.popret[	 ]+\{ra\},32
+[	 ]*[0-9a-f]+:[	 ]+be4e[	 ]+cm.popret[	 ]+\{ra\},64
+[	 ]*[0-9a-f]+:[	 ]+be72[	 ]+cm.popret[	 ]+\{ra,s0-s2\},32
+[	 ]*[0-9a-f]+:[	 ]+be7a[	 ]+cm.popret[	 ]+\{ra,s0-s2\},64
+[	 ]*[0-9a-f]+:[	 ]+be7e[	 ]+cm.popret[	 ]+\{ra,s0-s2\},80
+[	 ]*[0-9a-f]+:[	 ]+be82[	 ]+cm.popret[	 ]+\{ra,s0-s3\},48
+[	 ]*[0-9a-f]+:[	 ]+be86[	 ]+cm.popret[	 ]+\{ra,s0-s3\},64
+[	 ]*[0-9a-f]+:[	 ]+be8e[	 ]+cm.popret[	 ]+\{ra,s0-s3\},96
+[	 ]*[0-9a-f]+:[	 ]+beb2[	 ]+cm.popret[	 ]+\{ra,s0-s6\},64
+[	 ]*[0-9a-f]+:[	 ]+beb6[	 ]+cm.popret[	 ]+\{ra,s0-s6\},80
+[	 ]*[0-9a-f]+:[	 ]+bebe[	 ]+cm.popret[	 ]+\{ra,s0-s6\},112
+[	 ]*[0-9a-f]+:[	 ]+bec2[	 ]+cm.popret[	 ]+\{ra,s0-s7\},80
+[	 ]*[0-9a-f]+:[	 ]+bec6[	 ]+cm.popret[	 ]+\{ra,s0-s7\},96
+[	 ]*[0-9a-f]+:[	 ]+bece[	 ]+cm.popret[	 ]+\{ra,s0-s7\},128
+[	 ]*[0-9a-f]+:[	 ]+bee2[	 ]+cm.popret[	 ]+\{ra,s0-s9\},96
+[	 ]*[0-9a-f]+:[	 ]+bee6[	 ]+cm.popret[	 ]+\{ra,s0-s9\},112
+[	 ]*[0-9a-f]+:[	 ]+beee[	 ]+cm.popret[	 ]+\{ra,s0-s9\},144
+[	 ]*[0-9a-f]+:[	 ]+bef2[	 ]+cm.popret[	 ]+\{ra,s0-s11\},112
+[	 ]*[0-9a-f]+:[	 ]+bef6[	 ]+cm.popret[	 ]+\{ra,s0-s11\},128
+[	 ]*[0-9a-f]+:[	 ]+befa[	 ]+cm.popret[	 ]+\{ra,s0-s11\},144
+[	 ]*[0-9a-f]+:[	 ]+befe[	 ]+cm.popret[	 ]+\{ra,s0-s11\},160
+[	 ]*[0-9a-f]+:[	 ]+bc4e[	 ]+cm.popretz[	 ]+\{ra\},64
+[	 ]*[0-9a-f]+:[	 ]+bc5e[	 ]+cm.popretz[	 ]+\{ra,s0\},64
+[	 ]*[0-9a-f]+:[	 ]+bc6a[	 ]+cm.popretz[	 ]+\{ra,s0-s1\},64
+[	 ]*[0-9a-f]+:[	 ]+bc7a[	 ]+cm.popretz[	 ]+\{ra,s0-s2\},64
+[	 ]*[0-9a-f]+:[	 ]+bcda[	 ]+cm.popretz[	 ]+\{ra,s0-s8\},112
+[	 ]*[0-9a-f]+:[	 ]+bce6[	 ]+cm.popretz[	 ]+\{ra,s0-s9\},112
+[	 ]*[0-9a-f]+:[	 ]+bcf2[	 ]+cm.popretz[	 ]+\{ra,s0-s11\},112
+[	 ]*[0-9a-f]+:[	 ]+bc4e[	 ]+cm.popretz[	 ]+\{ra\},64
+[	 ]*[0-9a-f]+:[	 ]+bc5e[	 ]+cm.popretz[	 ]+\{ra,s0\},64
+[	 ]*[0-9a-f]+:[	 ]+bc6a[	 ]+cm.popretz[	 ]+\{ra,s0-s1\},64
+[	 ]*[0-9a-f]+:[	 ]+bc7a[	 ]+cm.popretz[	 ]+\{ra,s0-s2\},64
+[	 ]*[0-9a-f]+:[	 ]+bcda[	 ]+cm.popretz[	 ]+\{ra,s0-s8\},112
+[	 ]*[0-9a-f]+:[	 ]+bce6[	 ]+cm.popretz[	 ]+\{ra,s0-s9\},112
+[	 ]*[0-9a-f]+:[	 ]+bcf2[	 ]+cm.popretz[	 ]+\{ra,s0-s11},112
+[	 ]*[0-9a-f]+:[	 ]+bc42[	 ]+cm.popretz[	 ]+\{ra\},16
+[	 ]*[0-9a-f]+:[	 ]+bc46[	 ]+cm.popretz[	 ]+\{ra\},32
+[	 ]*[0-9a-f]+:[	 ]+bc4e[	 ]+cm.popretz[	 ]+\{ra\},64
+[	 ]*[0-9a-f]+:[	 ]+bc72[	 ]+cm.popretz[	 ]+\{ra,s0-s2\},32
+[	 ]*[0-9a-f]+:[	 ]+bc7a[	 ]+cm.popretz[	 ]+\{ra,s0-s2\},64
+[	 ]*[0-9a-f]+:[	 ]+bc7e[	 ]+cm.popretz[	 ]+\{ra,s0-s2\},80
+[	 ]*[0-9a-f]+:[	 ]+bc82[	 ]+cm.popretz[	 ]+\{ra,s0-s3\},48
+[	 ]*[0-9a-f]+:[	 ]+bc86[	 ]+cm.popretz[	 ]+\{ra,s0-s3\},64
+[	 ]*[0-9a-f]+:[	 ]+bc8e[	 ]+cm.popretz[	 ]+\{ra,s0-s3\},96
+[	 ]*[0-9a-f]+:[	 ]+bcb2[	 ]+cm.popretz[	 ]+\{ra,s0-s6\},64
+[	 ]*[0-9a-f]+:[	 ]+bcb6[	 ]+cm.popretz[	 ]+\{ra,s0-s6\},80
+[	 ]*[0-9a-f]+:[	 ]+bcbe[	 ]+cm.popretz[	 ]+\{ra,s0-s6\},112
+[	 ]*[0-9a-f]+:[	 ]+bcc2[	 ]+cm.popretz[	 ]+\{ra,s0-s7\},80
+[	 ]*[0-9a-f]+:[	 ]+bcc6[	 ]+cm.popretz[	 ]+\{ra,s0-s7\},96
+[	 ]*[0-9a-f]+:[	 ]+bcce[	 ]+cm.popretz[	 ]+\{ra,s0-s7\},128
+[	 ]*[0-9a-f]+:[	 ]+bce2[	 ]+cm.popretz[	 ]+\{ra,s0-s9\},96
+[	 ]*[0-9a-f]+:[	 ]+bce6[	 ]+cm.popretz[	 ]+\{ra,s0-s9\},112
+[	 ]*[0-9a-f]+:[	 ]+bcee[	 ]+cm.popretz[	 ]+\{ra,s0-s9\},144
+[	 ]*[0-9a-f]+:[	 ]+bcf2[	 ]+cm.popretz[	 ]+\{ra,s0-s11\},112
+[	 ]*[0-9a-f]+:[	 ]+bcf6[	 ]+cm.popretz[	 ]+\{ra,s0-s11\},128
+[	 ]*[0-9a-f]+:[	 ]+bcfa[	 ]+cm.popretz[	 ]+\{ra,s0-s11\},144
+[	 ]*[0-9a-f]+:[	 ]+bcfe[	 ]+cm.popretz[	 ]+\{ra,s0-s11\},160
diff --git a/gas/testsuite/gas/riscv/zcmp-push-pop.s b/gas/testsuite/gas/riscv/zcmp-push-pop.s
new file mode 100644
index 00000000000..dc441bc2776
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmp-push-pop.s
@@ -0,0 +1,162 @@
+target:
+
+	# push
+	# abi names
+	cm.push {ra}, -64
+	cm.push {ra, s0}, -64
+	cm.push {ra, s0-s1}, -64
+	cm.push {ra, s0-s2}, -64
+	cm.push {ra, s0-s8}, -112
+	cm.push {ra, s0-s9}, -112
+	cm.push {ra, s0-s11}, -112
+	# numeric names
+	cm.push {x1}, -64
+	cm.push {x1, x8}, -64
+	cm.push {x1, x8-x9}, -64
+	cm.push {x1, x8-x9, x18}, -64
+	cm.push {x1, x8-x9, x18-x24}, -112
+	cm.push {x1, x8-x9, x18-x25}, -112
+	cm.push {x1, x8-x9, x18-x27}, -112
+	# spimm
+	cm.push {ra}, -16
+	cm.push {ra}, -32
+	cm.push {ra}, -64
+	cm.push {ra, s0-s2}, -32
+	cm.push {ra, s0-s2}, -64
+	cm.push {ra, s0-s2}, -80
+	cm.push {ra, s0-s3}, -48
+	cm.push {ra, s0-s3}, -64
+	cm.push {ra, s0-s3}, -96
+	cm.push {ra, s0-s6}, -64
+	cm.push {ra, s0-s6}, -80
+	cm.push {ra, s0-s6}, -112
+	cm.push {ra, s0-s7}, -80
+	cm.push {ra, s0-s7}, -96
+	cm.push {ra, s0-s7}, -128
+	cm.push {ra, s0-s9}, -96
+	cm.push {ra, s0-s9}, -112
+	cm.push {ra, s0-s9}, -144
+	cm.push {ra, s0-s11}, -112
+	cm.push {ra, s0-s11}, -128
+	cm.push {ra, s0-s11}, -144
+	cm.push {ra, s0-s11}, -160
+	# pop
+	# abi names
+	cm.pop {ra}, 64
+	cm.pop {ra, s0}, 64
+	cm.pop {ra, s0-s1}, 64
+	cm.pop {ra, s0-s2}, 64
+	cm.pop {ra, s0-s8}, 112
+	cm.pop {ra, s0-s9}, 112
+	cm.pop {ra, s0-s11}, 112
+	# numeric names
+	cm.pop {x1}, 64
+	cm.pop {x1, x8}, 64
+	cm.pop {x1, x8-x9}, 64
+	cm.pop {x1, x8-x9, x18}, 64
+	cm.pop {x1, x8-x9, x18-x24}, 112
+	cm.pop {x1, x8-x9, x18-x25}, 112
+	cm.pop {x1, x8-x9, x18-x27}, 112
+	# spimm
+	cm.pop {ra}, 16
+	cm.pop {ra}, 32
+	cm.pop {ra}, 64
+	cm.pop {ra, s0-s2}, 32
+	cm.pop {ra, s0-s2}, 64
+	cm.pop {ra, s0-s2}, 80
+	cm.pop {ra, s0-s3}, 48
+	cm.pop {ra, s0-s3}, 64
+	cm.pop {ra, s0-s3}, 96
+	cm.pop {ra, s0-s6}, 64
+	cm.pop {ra, s0-s6}, 80
+	cm.pop {ra, s0-s6}, 112
+	cm.pop {ra, s0-s7}, 80
+	cm.pop {ra, s0-s7}, 96
+	cm.pop {ra, s0-s7}, 128
+	cm.pop {ra, s0-s9}, 96
+	cm.pop {ra, s0-s9}, 112
+	cm.pop {ra, s0-s9}, 144
+	cm.pop {ra, s0-s11}, 112
+	cm.pop {ra, s0-s11}, 128
+	cm.pop {ra, s0-s11}, 144
+	cm.pop {ra, s0-s11}, 160
+	# popret
+	# abi names
+	cm.popret {ra}, 64
+	cm.popret {ra, s0}, 64
+	cm.popret {ra, s0-s1}, 64
+	cm.popret {ra, s0-s2}, 64
+	cm.popret {ra, s0-s8}, 112
+	cm.popret {ra, s0-s9}, 112
+	cm.popret {ra, s0-s11}, 112
+	# numeric names
+	cm.popret {x1}, 64
+	cm.popret {x1, x8}, 64
+	cm.popret {x1, x8-x9}, 64
+	cm.popret {x1, x8-x9, x18}, 64
+	cm.popret {x1, x8-x9, x18-x24}, 112
+	cm.popret {x1, x8-x9, x18-x25}, 112
+	cm.popret {x1, x8-x9, x18-x27}, 112
+	# spimm
+	cm.popret {ra}, 16
+	cm.popret {ra}, 32
+	cm.popret {ra}, 64
+	cm.popret {ra, s0-s2}, 32
+	cm.popret {ra, s0-s2}, 64
+	cm.popret {ra, s0-s2}, 80
+	cm.popret {ra, s0-s3}, 48
+	cm.popret {ra, s0-s3}, 64
+	cm.popret {ra, s0-s3}, 96
+	cm.popret {ra, s0-s6}, 64
+	cm.popret {ra, s0-s6}, 80
+	cm.popret {ra, s0-s6}, 112
+	cm.popret {ra, s0-s7}, 80
+	cm.popret {ra, s0-s7}, 96
+	cm.popret {ra, s0-s7}, 128
+	cm.popret {ra, s0-s9}, 96
+	cm.popret {ra, s0-s9}, 112
+	cm.popret {ra, s0-s9}, 144
+	cm.popret {ra, s0-s11}, 112
+	cm.popret {ra, s0-s11}, 128
+	cm.popret {ra, s0-s11}, 144
+	cm.popret {ra, s0-s11}, 160
+	# popretz
+	# abi names
+	cm.popretz {ra}, 64
+	cm.popretz {ra, s0}, 64
+	cm.popretz {ra, s0-s1}, 64
+	cm.popretz {ra, s0-s2}, 64
+	cm.popretz {ra, s0-s8}, 112
+	cm.popretz {ra, s0-s9}, 112
+	cm.popretz {ra, s0-s11}, 112
+	# numeric names
+	cm.popretz {x1}, 64
+	cm.popretz {x1, x8}, 64
+	cm.popretz {x1, x8-x9}, 64
+	cm.popretz {x1, x8-x9, x18}, 64
+	cm.popretz {x1, x8-x9, x18-x24}, 112
+	cm.popretz {x1, x8-x9, x18-x25}, 112
+	cm.popretz {x1, x8-x9, x18-x27}, 112
+	# spimm
+	cm.popretz {ra}, 16
+	cm.popretz {ra}, 32
+	cm.popretz {ra}, 64
+	cm.popretz {ra, s0-s2}, 32
+	cm.popretz {ra, s0-s2}, 64
+	cm.popretz {ra, s0-s2}, 80
+	cm.popretz {ra, s0-s3}, 48
+	cm.popretz {ra, s0-s3}, 64
+	cm.popretz {ra, s0-s3}, 96
+	cm.popretz {ra, s0-s6}, 64
+	cm.popretz {ra, s0-s6}, 80
+	cm.popretz {ra, s0-s6}, 112
+	cm.popretz {ra, s0-s7}, 80
+	cm.popretz {ra, s0-s7}, 96
+	cm.popretz {ra, s0-s7}, 128
+	cm.popretz {ra, s0-s9}, 96
+	cm.popretz {ra, s0-s9}, 112
+	cm.popretz {ra, s0-s9}, 144
+	cm.popretz {ra, s0-s11}, 112
+	cm.popretz {ra, s0-s11}, 128
+	cm.popretz {ra, s0-s11}, 144
+	cm.popretz {ra, s0-s11}, 160
diff --git a/include/opcode/riscv-opc.h b/include/opcode/riscv-opc.h
index 24217062edc..9161dc371bc 100644
--- a/include/opcode/riscv-opc.h
+++ b/include/opcode/riscv-opc.h
@@ -2235,6 +2235,15 @@
 #define MASK_C_NOT 0xfc7f
 #define MATCH_C_MUL 0x9c41
 #define MASK_C_MUL 0xfc63
+/* Zcmp instructions.  */
+#define MATCH_CM_PUSH 0xb802
+#define MASK_CM_PUSH 0xff03
+#define MATCH_CM_POP 0xba02
+#define MASK_CM_POP 0xff03
+#define MATCH_CM_POPRET 0xbe02
+#define MASK_CM_POPRET 0xff03
+#define MATCH_CM_POPRETZ 0xbc02
+#define MASK_CM_POPRETZ 0xff03
 /* Svinval instruction.  */
 #define MATCH_SINVAL_VMA 0x16000073
 #define MASK_SINVAL_VMA 0xfe007fff
@@ -3538,6 +3547,11 @@ DECLARE_INSN(c_lhu, MATCH_C_LHU, MASK_C_LHU)
 DECLARE_INSN(c_lh, MATCH_C_LH, MASK_C_LH)
 DECLARE_INSN(c_sb, MATCH_C_SB, MASK_C_SB)
 DECLARE_INSN(c_sh, MATCH_C_SH, MASK_C_SH)
+/* Zcmp instructions.  */
+DECLARE_INSN(cm_push, MATCH_CM_PUSH, MASK_CM_PUSH)
+DECLARE_INSN(cm_pop, MATCH_CM_POP, MASK_CM_POP)
+DECLARE_INSN(cm_popret, MATCH_CM_POPRET, MASK_CM_POPRET)
+DECLARE_INSN(cm_popretz, MATCH_CM_POPRETZ, MASK_CM_POPRETZ)
 /* 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 710a9b73189..7b1ed47aa5d 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -24,6 +24,7 @@
 #include "riscv-opc.h"
 #include <stdlib.h>
 #include <stdint.h>
+#include <stdio.h>
 
 typedef uint64_t insn_t;
 
@@ -112,6 +113,8 @@ static inline unsigned int riscv_insn_length (insn_t insn)
   (RV_X(x, 6, 1) | (RV_X(x, 5, 1) << 1))
 #define EXTRACT_ZCB_HALFWORD_UIMM(x) \
   (RV_X(x, 5, 1) << 1)
+#define EXTRACT_ZCMP_SPIMM(x) \
+  (RV_X(x, 2, 2) << 4)
 /* Vendor-specific (CORE-V) extract macros.  */
 #define EXTRACT_CV_IS2_UIMM5(x) \
   (RV_X(x, 20, 5))
@@ -168,6 +171,8 @@ static inline unsigned int riscv_insn_length (insn_t insn)
   ((RV_X(x, 0, 1) << 6) | (RV_X(x, 1, 1) << 5))
 #define ENCODE_ZCB_HALFWORD_UIMM(x) \
   (RV_X(x, 1, 1) << 5)
+#define ENCODE_ZCMP_SPIMM(x) \
+  (RV_X(x, 4, 2) << 2)
 /* Vendor-specific (CORE-V) encode macros.  */
 #define ENCODE_CV_IS2_UIMM5(x) \
   (RV_X(x, 0, 5) << 20)
@@ -200,6 +205,7 @@ static inline unsigned int riscv_insn_length (insn_t insn)
 #define VALID_RVV_VC_IMM(x) (EXTRACT_RVV_VC_IMM(ENCODE_RVV_VC_IMM(x)) == (x))
 #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))
 
 #define RISCV_RTYPE(insn, rd, rs1, rs2) \
   ((MATCH_ ## insn) | ((rd) << OP_SH_RD) | ((rs1) << OP_SH_RS1) | ((rs2) << OP_SH_RS2))
@@ -328,6 +334,11 @@ static inline unsigned int riscv_insn_length (insn_t insn)
 #define OP_MASK_VWD		0x1
 #define OP_SH_VWD		26
 
+/* Zc fields.  */
+#define OP_MASK_RLIST		0xf
+#define OP_SH_RLIST		4
+#define SP_ALIGNMENT 16
+
 #define NVECR 32
 #define NVECM 1
 
@@ -340,6 +351,11 @@ static inline unsigned int riscv_insn_length (insn_t insn)
 #define X_T0 5
 #define X_T1 6
 #define X_T2 7
+#define X_S0 8
+#define X_S1 9
+#define X_S2 18
+#define X_S10 26
+#define X_S11 27
 #define X_T3 28
 
 #define NGPR 32
@@ -448,6 +464,7 @@ enum riscv_insn_class
   INSN_CLASS_ZCB_AND_ZBA,
   INSN_CLASS_ZCB_AND_ZBB,
   INSN_CLASS_ZCB_AND_ZMMUL,
+  INSN_CLASS_ZCMP,
   INSN_CLASS_SVINVAL,
   INSN_CLASS_ZICBOM,
   INSN_CLASS_ZICBOP,
@@ -564,6 +581,19 @@ enum
   M_NUM_MACROS
 };
 
+/* get sp base adjustment */
+
+static inline unsigned int
+riscv_get_sp_base (insn_t opcode, unsigned int xlen)
+{
+  unsigned reg_size = xlen / 8;
+  unsigned rlist = EXTRACT_BITS (opcode, OP_MASK_RLIST, OP_SH_RLIST);
+
+  unsigned min_sp_adj = (rlist - 3) * reg_size + (rlist == 15 ? reg_size : 0);
+  return ((min_sp_adj / SP_ALIGNMENT) + (min_sp_adj % SP_ALIGNMENT != 0))
+    * SP_ALIGNMENT;
+}
+
 /* The mapping symbol states.  */
 enum riscv_seg_mstate
 {
diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c
index ca328b4c997..810b24f5796 100644
--- a/opcodes/riscv-dis.c
+++ b/opcodes/riscv-dis.c
@@ -75,6 +75,8 @@ static const char (*riscv_fpr_names)[NRC];
 /* If set, disassemble as most general instruction.  */
 static bool no_aliases = false;
 
+/* If set, disassemble numeric register names instead of ABI names.  */
+static int numeric = false;
 
 /* Set default RISC-V disassembler options.  */
 
@@ -84,6 +86,7 @@ set_default_riscv_dis_options (void)
   riscv_gpr_names = riscv_gpr_names_abi;
   riscv_fpr_names = riscv_fpr_names_abi;
   no_aliases = false;
+  numeric = false;
 }
 
 /* Parse RISC-V disassembler option (without arguments).  */
@@ -97,6 +100,7 @@ parse_riscv_dis_option_without_args (const char *option)
     {
       riscv_gpr_names = riscv_gpr_names_numeric;
       riscv_fpr_names = riscv_fpr_names_numeric;
+      numeric = true;
     }
   else
     return false;
@@ -215,6 +219,50 @@ maybe_print_address (struct riscv_private_data *pd, int base_reg, int offset,
     pd->print_addr = (bfd_vma)(uint32_t)pd->print_addr;
 }
 
+/* Get Zcmp rlist field.  */
+
+static void
+print_rlist (disassemble_info *info, insn_t l)
+{
+  unsigned rlist = (int)EXTRACT_OPERAND (RLIST, l);
+  unsigned r_start = numeric ? X_S2 : X_S0;
+  info->fprintf_func (info->stream, "%s", riscv_gpr_names[X_RA]);
+
+  if (rlist == 5)
+    info->fprintf_func (info->stream, ",%s", riscv_gpr_names[X_S0]);
+  else if (rlist == 6 || (numeric && rlist > 6))
+    info->fprintf_func (info->stream, ",%s-%s",
+	  riscv_gpr_names[X_S0],
+	  riscv_gpr_names[X_S1]);
+
+  if (rlist == 15)
+    info->fprintf_func (info->stream, ",%s-%s",
+	  riscv_gpr_names[r_start],
+	  riscv_gpr_names[X_S11]);
+  else if (rlist == 7 && numeric)
+    info->fprintf_func (info->stream, ",%s",
+	  riscv_gpr_names[X_S2]);
+  else if (rlist > 6)
+    info->fprintf_func (info->stream, ",%s-%s",
+	  riscv_gpr_names[r_start],
+	  riscv_gpr_names[rlist + 11]);
+}
+
+/* Get Zcmp sp adjustment immediate.  */
+
+static int
+riscv_get_spimm (insn_t l)
+{
+  int spimm = riscv_get_sp_base(l, *riscv_rps_dis.xlen);
+
+  spimm += EXTRACT_ZCMP_SPIMM (l);
+
+  if (((l ^ MATCH_CM_PUSH) & MASK_CM_PUSH) == 0)
+    spimm *= -1;
+
+  return spimm;
+}
+
 /* Print insn arguments for 32/64-bit code.  */
 
 static void
@@ -420,6 +468,8 @@ print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
 	case ')':
 	case '[':
 	case ']':
+	case '{':
+	case '}':
 	  print (info->stream, dis_style_text, "%c", *oparg);
 	  break;
 
@@ -624,6 +674,13 @@ print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
 		  print (info->stream, dis_style_immediate, "%d",
 			 (int)EXTRACT_ZCB_HALFWORD_UIMM (l));
 		  break;
+		case 'r':
+		  print_rlist (info, l);
+		  break;
+		case 'p':
+		  print (info->stream, dis_style_immediate, "%d",
+			 riscv_get_spimm (l));
+		  break;
 		default:
 		  goto undefined_modifier;
 		}
diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
index 72d727cd77e..fe700dbd609 100644
--- a/opcodes/riscv-opc.c
+++ b/opcodes/riscv-opc.c
@@ -1981,6 +1981,12 @@ const struct riscv_opcode riscv_opcodes[] =
 {"c.zext.b",   0, INSN_CLASS_ZCB, "Cs",  MATCH_C_ZEXT_B, MASK_C_ZEXT_B, match_opcode, 0 },
 {"c.sext.w",  64, INSN_CLASS_ZCB, "d",  MATCH_C_ADDIW, MASK_C_ADDIW|MASK_RVC_IMM, match_rd_nonzero, INSN_ALIAS },
 
+/* Zcmp instructions.  */
+{"cm.push",    0,  INSN_CLASS_ZCMP, "{Wcr},Wcp",  MATCH_CM_PUSH, MASK_CM_PUSH, match_opcode, 0 },
+{"cm.pop",     0,  INSN_CLASS_ZCMP, "{Wcr},Wcp",  MATCH_CM_POP, MASK_CM_POP, match_opcode, 0 },
+{"cm.popret",  0,  INSN_CLASS_ZCMP, "{Wcr},Wcp",  MATCH_CM_POPRET, MASK_CM_POPRET, match_opcode, 0 },
+{"cm.popretz", 0,  INSN_CLASS_ZCMP, "{Wcr},Wcp",  MATCH_CM_POPRETZ, MASK_CM_POPRETZ, match_opcode, 0 },
+
 /* Supervisor instructions.  */
 {"csrr",       0, INSN_CLASS_ZICSR, "d,E",   MATCH_CSRRS, MASK_CSRRS|MASK_RS1, match_opcode, INSN_ALIAS },
 {"csrwi",      0, INSN_CLASS_ZICSR, "E,Z",   MATCH_CSRRWI, MASK_CSRRWI|MASK_RD, match_opcode, INSN_ALIAS },
-- 
2.25.1


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

* [PATCH v3 2/2] RISC-V: Support Zcmp cm.mv instructions.
  2023-11-20  7:06 [PATCH v3 1/2] RISC-V: Support Zcmp push/pop instructions Jiawei
@ 2023-11-20  7:06 ` Jiawei
  2024-01-05  7:02   ` Kito Cheng
  2024-01-05  7:01 ` [PATCH v3 1/2] RISC-V: Support Zcmp push/pop instructions Kito Cheng
  1 sibling, 1 reply; 4+ messages in thread
From: Jiawei @ 2023-11-20  7:06 UTC (permalink / raw)
  To: binutils
  Cc: nelson, kito.cheng, palmer, jbeulich, research_trasio,
	christoph.muellner, jeremy.bennett, nandni.jamnadas,
	mary.bennett, charlie.keaney, simon.cook, sinan.lin, gaofei,
	fujin.zhao, wuwei2016, shihua, shiyulong, chenyixuan, Jiawei

This patch supports Zcmp instruction 'cm.mva01s' and 'cm.mvsa01'.
All disassemble instructions use the sreg format.

Co-Authored by: Charlie Keaney <charlie.keaney@embecosm.com>
Co-Authored by: Mary Bennett <mary.bennett@embecosm.com>
Co-Authored by: Nandni Jamnadas <nandni.jamnadas@embecosm.com>
Co-Authored by: Sinan Lin <sinan.lin@linux.alibaba.com>
Co-Authored by: Simon Cook <simon.cook@embecosm.com>
Co-Authored by: Shihua Liao <shihua@iscas.ac.cn>
Co-Authored by: Yulong Shi <yulong@iscas.ac.cn>

gas/ChangeLog:

        * config/tc-riscv.c (validate_riscv_insn): New operators.
        (riscv_ip): Ditto.
        * testsuite/gas/riscv/zcmp-mv.d: New test.
        * testsuite/gas/riscv/zcmp-mv.s: New test.

include/ChangeLog:

        * opcode/riscv-opc.h (MATCH_CM_MVA01S): New opcode.
        (MASK_CM_MVA01S): New mask.
        (MATCH_CM_MVSA01): New opcode.
        (MASK_CM_MVSA01): New mask.
        (DECLARE_INSN): New declarations.
        * opcode/riscv.h (OP_MASK_SREG1): New mask.
        (OP_SH_SREG1): New operand code.
        (OP_MASK_SREG2): New mask.
        (OP_SH_SREG2): New operand code.
        (X_A0): New reg number.
        (X_A1): Ditto.
        (X_S7): Ditto.
        (RISCV_SREG_0_7): New macro function.

opcodes/ChangeLog:

        * riscv-dis.c (riscv_zcmp_get_sregno): New function.
        (print_insn_args): New operators.
        * riscv-opc.c (match_sreg1_not_eq_sreg2): New match function.

---
 gas/config/tc-riscv.c             | 15 +++++++++++++++
 gas/testsuite/gas/riscv/zcmp-mv.d | 26 ++++++++++++++++++++++++++
 gas/testsuite/gas/riscv/zcmp-mv.s | 21 +++++++++++++++++++++
 include/opcode/riscv-opc.h        |  6 ++++++
 include/opcode/riscv.h            | 12 ++++++++++++
 opcodes/riscv-dis.c               | 19 +++++++++++++++++++
 opcodes/riscv-opc.c               |  9 +++++++++
 7 files changed, 108 insertions(+)
 create mode 100644 gas/testsuite/gas/riscv/zcmp-mv.d
 create mode 100644 gas/testsuite/gas/riscv/zcmp-mv.s

diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index a46de00fc32..89770b45dac 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -1559,6 +1559,9 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
 	    case 'c':
 	      switch (*++oparg)
 		{
+		/* sreg operators in cm.mvsa01 and cm.mva01s. */
+		case '1': USE_BITS (OP_MASK_SREG1, OP_SH_SREG1); break;
+		case '2': USE_BITS (OP_MASK_SREG2, OP_SH_SREG2); break;
 		/* byte immediate operators, load/store byte insns.  */
 		case 'h': used_bits |= ENCODE_ZCB_HALFWORD_UIMM (-1U); break;
 		/* halfword immediate operators, load/store halfword insns.  */
@@ -3679,6 +3682,18 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
 		      asarg = expr_parse_end;
 		      imm_expr->X_op = O_absent;
 		      continue;
+		    case '1':
+		      if (!reg_lookup (&asarg, RCLASS_GPR, &regno)
+			  || !RISCV_SREG_0_7 (regno))
+			break;
+		      INSERT_OPERAND (SREG1, *ip, regno % 8);
+		      continue;
+		    case '2':
+		      if (!reg_lookup (&asarg, RCLASS_GPR, &regno)
+			  || !RISCV_SREG_0_7 (regno))
+			break;
+		      INSERT_OPERAND (SREG2, *ip, regno % 8);
+		      continue;
 		    default:
 		      goto unknown_riscv_ip_operand;
 		    }
diff --git a/gas/testsuite/gas/riscv/zcmp-mv.d b/gas/testsuite/gas/riscv/zcmp-mv.d
new file mode 100644
index 00000000000..351d301dd3f
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmp-mv.d
@@ -0,0 +1,26 @@
+#as: -march=rv64i_zcmp
+#source: zcmp-mv.s
+#objdump: -dr -Mno-aliases
+
+.*:[	 ]+file format .*
+
+
+Disassembly of section .text:
+
+0+000 <target>:
+[	 ]*[0-9a-f]+:[	 ]+ac7e[	 ]+cm.mva01s[	 ]+s0,s7
+[	 ]*[0-9a-f]+:[	 ]+ac7a[	 ]+cm.mva01s[	 ]+s0,s6
+[	 ]*[0-9a-f]+:[	 ]+acfe[	 ]+cm.mva01s[	 ]+s1,s7
+[	 ]*[0-9a-f]+:[	 ]+acfa[	 ]+cm.mva01s[	 ]+s1,s6
+[	 ]*[0-9a-f]+:[	 ]+afee[	 ]+cm.mva01s[	 ]+s7,s3
+[	 ]*[0-9a-f]+:[	 ]+ade2[	 ]+cm.mva01s[	 ]+s3,s0
+[	 ]*[0-9a-f]+:[	 ]+aef2[	 ]+cm.mva01s[	 ]+s5,s4
+[	 ]*[0-9a-f]+:[	 ]+aefa[	 ]+cm.mva01s[	 ]+s5,s6
+[	 ]*[0-9a-f]+:[	 ]+afa2[	 ]+cm.mvsa01[	 ]+s7,s0
+[	 ]*[0-9a-f]+:[	 ]+af22[	 ]+cm.mvsa01[	 ]+s6,s0
+[	 ]*[0-9a-f]+:[	 ]+afa6[	 ]+cm.mvsa01[	 ]+s7,s1
+[	 ]*[0-9a-f]+:[	 ]+af26[	 ]+cm.mvsa01[	 ]+s6,s1
+[	 ]*[0-9a-f]+:[	 ]+adbe[	 ]+cm.mvsa01[	 ]+s3,s7
+[	 ]*[0-9a-f]+:[	 ]+ada2[	 ]+cm.mvsa01[	 ]+s3,s0
+[	 ]*[0-9a-f]+:[	 ]+aeb2[	 ]+cm.mvsa01[	 ]+s5,s4
+[	 ]*[0-9a-f]+:[	 ]+aeba[	 ]+cm.mvsa01[	 ]+s5,s6
diff --git a/gas/testsuite/gas/riscv/zcmp-mv.s b/gas/testsuite/gas/riscv/zcmp-mv.s
new file mode 100644
index 00000000000..0bcf2a6cd98
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zcmp-mv.s
@@ -0,0 +1,21 @@
+target:
+
+	# cm.mva01s
+	cm.mva01s s0,s7
+	cm.mva01s s0,s6
+	cm.mva01s s1,s7
+	cm.mva01s s1,s6
+	cm.mva01s s7,s3
+	cm.mva01s x19,s0
+	cm.mva01s s5,x20
+	cm.mva01s x21,x22
+
+	# cm.mvsa01
+	cm.mvsa01 s7,s0
+	cm.mvsa01 s6,s0
+	cm.mvsa01 s7,s1
+	cm.mvsa01 s6,s1
+	cm.mvsa01 s3,s7
+	cm.mvsa01 x19,s0
+	cm.mvsa01 s5,x20
+	cm.mvsa01 x21,x22
diff --git a/include/opcode/riscv-opc.h b/include/opcode/riscv-opc.h
index 9161dc371bc..8aeedf7f8f8 100644
--- a/include/opcode/riscv-opc.h
+++ b/include/opcode/riscv-opc.h
@@ -2244,6 +2244,10 @@
 #define MASK_CM_POPRET 0xff03
 #define MATCH_CM_POPRETZ 0xbc02
 #define MASK_CM_POPRETZ 0xff03
+#define MATCH_CM_MVA01S 0xac62
+#define MASK_CM_MVA01S 0xfc63
+#define MATCH_CM_MVSA01 0xac22
+#define MASK_CM_MVSA01 0xfc63
 /* Svinval instruction.  */
 #define MATCH_SINVAL_VMA 0x16000073
 #define MASK_SINVAL_VMA 0xfe007fff
@@ -3552,6 +3556,8 @@ DECLARE_INSN(cm_push, MATCH_CM_PUSH, MASK_CM_PUSH)
 DECLARE_INSN(cm_pop, MATCH_CM_POP, MASK_CM_POP)
 DECLARE_INSN(cm_popret, MATCH_CM_POPRET, MASK_CM_POPRET)
 DECLARE_INSN(cm_popretz, MATCH_CM_POPRETZ, MASK_CM_POPRETZ)
+DECLARE_INSN(cm_mvsa01, MATCH_CM_MVSA01, MASK_CM_MVSA01)
+DECLARE_INSN(cm_mva01s, MATCH_CM_MVA01S, MASK_CM_MVA01S)
 /* 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 7b1ed47aa5d..dead4fbdda1 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -338,6 +338,10 @@ static inline unsigned int riscv_insn_length (insn_t insn)
 #define OP_MASK_RLIST		0xf
 #define OP_SH_RLIST		4
 #define SP_ALIGNMENT 16
+#define OP_MASK_SREG1		0x7
+#define OP_SH_SREG1		7
+#define OP_MASK_SREG2		0x7
+#define OP_SH_SREG2		2
 
 #define NVECR 32
 #define NVECM 1
@@ -353,7 +357,10 @@ static inline unsigned int riscv_insn_length (insn_t insn)
 #define X_T2 7
 #define X_S0 8
 #define X_S1 9
+#define X_A0 10
+#define X_A1 11
 #define X_S2 18
+#define X_S7 23
 #define X_S10 26
 #define X_S11 27
 #define X_T3 28
@@ -401,6 +408,11 @@ static inline unsigned int riscv_insn_length (insn_t insn)
 /* The maximal number of subset can be required.  */
 #define MAX_SUBSET_NUM 4
 
+/* The range of sregs.  */
+#define RISCV_SREG_0_7(REGNO) \
+	((REGNO == X_S0 || REGNO == X_S1) \
+	 || (REGNO >= X_S2 && REGNO <= X_S7))
+
 /* All RISC-V instructions belong to at least one of these classes.  */
 enum riscv_insn_class
 {
diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c
index 810b24f5796..26914d8950c 100644
--- a/opcodes/riscv-dis.c
+++ b/opcodes/riscv-dis.c
@@ -263,6 +263,17 @@ riscv_get_spimm (insn_t l)
   return spimm;
 }
 
+/* Get s-register regno by using sreg number.
+   e.g. the regno of s0 is 8, so
+   riscv_zcmp_get_sregno (0) equals 8. */
+
+static unsigned
+riscv_zcmp_get_sregno (unsigned sreg_idx)
+{
+  return sreg_idx > 1 ?
+      sreg_idx + 16 : sreg_idx + 8;
+}
+
 /* Print insn arguments for 32/64-bit code.  */
 
 static void
@@ -666,6 +677,14 @@ print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
 	    case 'c': /* Zcb extension 16 bits length instruction fields. */
 	      switch (*++oparg)
 		{
+		case '1':
+		    print (info->stream, dis_style_register, "%s",
+		      riscv_gpr_names[riscv_zcmp_get_sregno (EXTRACT_OPERAND (SREG1, l))]);
+		    break;
+		case '2':
+		    print (info->stream, dis_style_register, "%s",
+		      riscv_gpr_names[riscv_zcmp_get_sregno (EXTRACT_OPERAND (SREG2, l))]);
+		    break;
 		case 'b':
 		  print (info->stream, dis_style_immediate, "%d",
 			 (int)EXTRACT_ZCB_BYTE_UIMM (l));
diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
index fe700dbd609..f5e736193b3 100644
--- a/opcodes/riscv-opc.c
+++ b/opcodes/riscv-opc.c
@@ -321,6 +321,13 @@ match_th_load_pair(const struct riscv_opcode *op,
   return rd1 != rd2 && rd1 != rs && rd2 != rs && match_opcode (op, insn);
 }
 
+static int
+match_sreg1_not_eq_sreg2 (const struct riscv_opcode *op, insn_t insn)
+{
+  return match_opcode (op, insn)
+      && (EXTRACT_OPERAND (SREG1, insn) != EXTRACT_OPERAND (SREG2, insn));
+}
+
 /* The order of overloaded instructions matters.  Label arguments and
    register arguments look the same. Instructions that can have either
    for arguments must apear in the correct order in this table for the
@@ -1986,6 +1993,8 @@ const struct riscv_opcode riscv_opcodes[] =
 {"cm.pop",     0,  INSN_CLASS_ZCMP, "{Wcr},Wcp",  MATCH_CM_POP, MASK_CM_POP, match_opcode, 0 },
 {"cm.popret",  0,  INSN_CLASS_ZCMP, "{Wcr},Wcp",  MATCH_CM_POPRET, MASK_CM_POPRET, match_opcode, 0 },
 {"cm.popretz", 0,  INSN_CLASS_ZCMP, "{Wcr},Wcp",  MATCH_CM_POPRETZ, MASK_CM_POPRETZ, match_opcode, 0 },
+{"cm.mva01s",  0,  INSN_CLASS_ZCMP, "Wc1,Wc2",    MATCH_CM_MVA01S, MASK_CM_MVA01S, match_opcode, 0 },
+{"cm.mvsa01",  0,  INSN_CLASS_ZCMP, "Wc1,Wc2",    MATCH_CM_MVSA01, MASK_CM_MVSA01, match_sreg1_not_eq_sreg2, 0 },
 
 /* Supervisor instructions.  */
 {"csrr",       0, INSN_CLASS_ZICSR, "d,E",   MATCH_CSRRS, MASK_CSRRS|MASK_RS1, match_opcode, INSN_ALIAS },
-- 
2.25.1


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

* Re: [PATCH v3 1/2] RISC-V: Support Zcmp push/pop instructions.
  2023-11-20  7:06 [PATCH v3 1/2] RISC-V: Support Zcmp push/pop instructions Jiawei
  2023-11-20  7:06 ` [PATCH v3 2/2] RISC-V: Support Zcmp cm.mv instructions Jiawei
@ 2024-01-05  7:01 ` Kito Cheng
  1 sibling, 0 replies; 4+ messages in thread
From: Kito Cheng @ 2024-01-05  7:01 UTC (permalink / raw)
  To: Jiawei
  Cc: binutils, nelson, kito.cheng, palmer, jbeulich, research_trasio,
	christoph.muellner, jeremy.bennett, nandni.jamnadas,
	mary.bennett, charlie.keaney, simon.cook, sinan.lin, gaofei,
	fujin.zhao, wuwei2016, shihua, shiyulong, chenyixuan

> diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
> index abcb409bd78..6f551bb6907 100644
> --- a/bfd/elfxx-riscv.h
> +++ b/bfd/elfxx-riscv.h
> @@ -26,6 +26,7 @@
>  #include "cpu-riscv.h"
>
>  #define RISCV_UNKNOWN_VERSION -1
> +#define SP_ALIGNMENT 16

Rename to ZCMP_SP_ALIGNMENT, since ilp32e may not align to 16, and
this macro may cause confusion .

>
>  struct riscv_elf_params
>  {
> diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
> index 402c46ad753..a46de00fc32 100644
> --- a/gas/config/tc-riscv.c
> +++ b/gas/config/tc-riscv.c
> @@ -1246,6 +1246,125 @@ flt_lookup (float f, const float *array, size_t size, unsigned *regnop)
>    return false;
>  }
>
> +/* Map ra and s-register to [4,15], so that we can check if the
> +   reg2 in register list reg1-reg2 or single reg2 is valid or not,
> +   and obtain the corresponding rlist value.

rlist -> reg_list

> +
> +   ra - 4
> +   s0 - 5
> +   s1 - 6
> +    ....
> +   s10 - 0 (invalid)
> +   s11 - 15.  */
> +
> +static int
> +regno_to_rlist (unsigned regno)

rlist -> reg_list

> +{
> +  if (regno == X_RA)
> +    return 4;
> +  else if (regno == X_S0 || regno == X_S1)
> +    return 5 + regno - X_S0;
> +  else if (regno >= X_S2 && regno < X_S10)
> +    return 7 + regno - X_S2;
> +  else if (regno == X_S11)
> +    return 15;
> +
> +  return 0; /* invalid symbol */
> +}
> +
> +/* Parse register list, and the parsed rlist value is stored in rlist
> +   argument.
> +
> +   If ABI register names are used (e.g. ra and s0), the register
> +   list could be "{ra}", "{ra, s0}", "{ra, s0-sN}", where 0 < N < 10 or
> +   N == 11.
> +
> +   If numeric register names are used (e.g. x1 and x8), the register list
> +   could be "{x1}", "{x1,x8}", "{x1,x8-x9}", "{x1,x8-x9,x18}" and
> +   "{x1,x8-x9,x18-xN}", where 19 < N < 25 or N == 27.
> +
> +   It will fail if numeric register names and ABI register names are used
> +   at the same time.
> +*/
> +
> +static bool
> +reglist_lookup (char **s, unsigned *rlist)

rlist -> reg_list

> +{
> +  unsigned regno = 0;
> +  unsigned regnum = 0;
> +  char *reglist = strdup(*s);

space before (, e.g. strdup (*s);

> +  char *regname[3];
> +
> +  if (reglist == NULL)
> +    return false;
> +
> +  reglist = strtok(reglist, "}");

space before (

> +  for(reglist = strtok(reglist, ",");reglist;reglist = strtok(NULL, ",")){

space before (

> +    regname[regnum] = reglist;
> +    regnum++;
> +  }
> +
> +  /* Use to check if the register format is xreg.  */
> +  bool use_xreg = **s == 'x';
> +
> +  /* The first register in register list should be ra.  */

in the register list

> +  if (!reg_lookup (s, RCLASS_GPR, &regno)
> +     || !(*rlist = regno_to_rlist (regno)) /* update rlist */
> +     || regno != X_RA)
> +    return false;
> +
> +  if (regnum == 1)
> +    return true;
> +
> +  /* Do not use numeric and abi names at the same time.  */
> +  if ((*++*s != 'x') && use_xreg)
> +    return false;
> +  /* Reg1 should be s0 or its numeric names x8.  */
> +  if (!reg_lookup (s, RCLASS_GPR, &regno)
> +     || !(*rlist = regno_to_rlist (regno))
> +     || regno != X_S0)
> +    return false;
> +
> +  if(strlen(regname[1]) == 2)

space before (

> +    return true;
> +
> +  if ((*++*s != 'x') && use_xreg)
> +    return false;
> +  /* Reg2 is x9 if the numeric name is used, otherwise,
> +    it could be any other sN register, where N > 0.  */
> +  if (!reg_lookup (s, RCLASS_GPR, &regno)
> +     || !(*rlist = regno_to_rlist (regno))
> +     || regno <= X_S0
> +     || (use_xreg && regno != X_S1))
> +    return false;
> +
> +  if (regnum == 2)
> +    return true;
> +
> +  if(regnum == 3 && use_xreg){

space before ( and space between ) {

 if (regnum == 3 && use_xreg) {

> +    if ((*++*s != 'x') && use_xreg)
> +      return false;
> +    /* Reg3 should be s2.  */
> +    if (!reg_lookup (s, RCLASS_GPR, &regno)
> +       || !(*rlist = regno_to_rlist (regno))
> +       || regno != X_S2)
> +      return false;
> +    if(strlen(regname[2]) == 3)
> +      return true;
> +    if ((*++*s != 'x') && use_xreg)
> +      return false;
> +    /* Reg4 could be any other sN register, where N > 1.  */
> +    if (!reg_lookup (s, RCLASS_GPR, &regno)
> +       || !(*rlist = regno_to_rlist (regno))
> +       || regno <= X_S2)
> +      return false;
> +    return true;
> +  }
> +
> +  free(reglist);

space before (

> +  return false;
> +}
> +
>  #define USE_BITS(mask,shift) (used_bits |= ((insn_t)(mask) << (shift)))
>  #define USE_IMM(n, s) \
>    (used_bits |= ((insn_t)((1ull<<n)-1) << (s)))
> diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
> index 710a9b73189..7b1ed47aa5d 100644
> --- a/include/opcode/riscv.h
> +++ b/include/opcode/riscv.h
> @@ -24,6 +24,7 @@
>  #include "riscv-opc.h"
>  #include <stdlib.h>
>  #include <stdint.h>
> +#include <stdio.h>

Why include stdio.h?

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

* Re: [PATCH v3 2/2] RISC-V: Support Zcmp cm.mv instructions.
  2023-11-20  7:06 ` [PATCH v3 2/2] RISC-V: Support Zcmp cm.mv instructions Jiawei
@ 2024-01-05  7:02   ` Kito Cheng
  0 siblings, 0 replies; 4+ messages in thread
From: Kito Cheng @ 2024-01-05  7:02 UTC (permalink / raw)
  To: Jiawei
  Cc: binutils, nelson, kito.cheng, palmer, jbeulich, research_trasio,
	christoph.muellner, jeremy.bennett, nandni.jamnadas,
	mary.bennett, charlie.keaney, simon.cook, sinan.lin, gaofei,
	fujin.zhao, wuwei2016, shihua, shiyulong, chenyixuan

LGTM

On Mon, Nov 20, 2023 at 3:07 PM Jiawei <jiawei@iscas.ac.cn> wrote:
>
> This patch supports Zcmp instruction 'cm.mva01s' and 'cm.mvsa01'.
> All disassemble instructions use the sreg format.
>
> Co-Authored by: Charlie Keaney <charlie.keaney@embecosm.com>
> Co-Authored by: Mary Bennett <mary.bennett@embecosm.com>
> Co-Authored by: Nandni Jamnadas <nandni.jamnadas@embecosm.com>
> Co-Authored by: Sinan Lin <sinan.lin@linux.alibaba.com>
> Co-Authored by: Simon Cook <simon.cook@embecosm.com>
> Co-Authored by: Shihua Liao <shihua@iscas.ac.cn>
> Co-Authored by: Yulong Shi <yulong@iscas.ac.cn>
>
> gas/ChangeLog:
>
>         * config/tc-riscv.c (validate_riscv_insn): New operators.
>         (riscv_ip): Ditto.
>         * testsuite/gas/riscv/zcmp-mv.d: New test.
>         * testsuite/gas/riscv/zcmp-mv.s: New test.
>
> include/ChangeLog:
>
>         * opcode/riscv-opc.h (MATCH_CM_MVA01S): New opcode.
>         (MASK_CM_MVA01S): New mask.
>         (MATCH_CM_MVSA01): New opcode.
>         (MASK_CM_MVSA01): New mask.
>         (DECLARE_INSN): New declarations.
>         * opcode/riscv.h (OP_MASK_SREG1): New mask.
>         (OP_SH_SREG1): New operand code.
>         (OP_MASK_SREG2): New mask.
>         (OP_SH_SREG2): New operand code.
>         (X_A0): New reg number.
>         (X_A1): Ditto.
>         (X_S7): Ditto.
>         (RISCV_SREG_0_7): New macro function.
>
> opcodes/ChangeLog:
>
>         * riscv-dis.c (riscv_zcmp_get_sregno): New function.
>         (print_insn_args): New operators.
>         * riscv-opc.c (match_sreg1_not_eq_sreg2): New match function.
>
> ---
>  gas/config/tc-riscv.c             | 15 +++++++++++++++
>  gas/testsuite/gas/riscv/zcmp-mv.d | 26 ++++++++++++++++++++++++++
>  gas/testsuite/gas/riscv/zcmp-mv.s | 21 +++++++++++++++++++++
>  include/opcode/riscv-opc.h        |  6 ++++++
>  include/opcode/riscv.h            | 12 ++++++++++++
>  opcodes/riscv-dis.c               | 19 +++++++++++++++++++
>  opcodes/riscv-opc.c               |  9 +++++++++
>  7 files changed, 108 insertions(+)
>  create mode 100644 gas/testsuite/gas/riscv/zcmp-mv.d
>  create mode 100644 gas/testsuite/gas/riscv/zcmp-mv.s
>
> diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
> index a46de00fc32..89770b45dac 100644
> --- a/gas/config/tc-riscv.c
> +++ b/gas/config/tc-riscv.c
> @@ -1559,6 +1559,9 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
>             case 'c':
>               switch (*++oparg)
>                 {
> +               /* sreg operators in cm.mvsa01 and cm.mva01s. */
> +               case '1': USE_BITS (OP_MASK_SREG1, OP_SH_SREG1); break;
> +               case '2': USE_BITS (OP_MASK_SREG2, OP_SH_SREG2); break;
>                 /* byte immediate operators, load/store byte insns.  */
>                 case 'h': used_bits |= ENCODE_ZCB_HALFWORD_UIMM (-1U); break;
>                 /* halfword immediate operators, load/store halfword insns.  */
> @@ -3679,6 +3682,18 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
>                       asarg = expr_parse_end;
>                       imm_expr->X_op = O_absent;
>                       continue;
> +                   case '1':
> +                     if (!reg_lookup (&asarg, RCLASS_GPR, &regno)
> +                         || !RISCV_SREG_0_7 (regno))
> +                       break;
> +                     INSERT_OPERAND (SREG1, *ip, regno % 8);
> +                     continue;
> +                   case '2':
> +                     if (!reg_lookup (&asarg, RCLASS_GPR, &regno)
> +                         || !RISCV_SREG_0_7 (regno))
> +                       break;
> +                     INSERT_OPERAND (SREG2, *ip, regno % 8);
> +                     continue;
>                     default:
>                       goto unknown_riscv_ip_operand;
>                     }
> diff --git a/gas/testsuite/gas/riscv/zcmp-mv.d b/gas/testsuite/gas/riscv/zcmp-mv.d
> new file mode 100644
> index 00000000000..351d301dd3f
> --- /dev/null
> +++ b/gas/testsuite/gas/riscv/zcmp-mv.d
> @@ -0,0 +1,26 @@
> +#as: -march=rv64i_zcmp
> +#source: zcmp-mv.s
> +#objdump: -dr -Mno-aliases
> +
> +.*:[    ]+file format .*
> +
> +
> +Disassembly of section .text:
> +
> +0+000 <target>:
> +[       ]*[0-9a-f]+:[   ]+ac7e[         ]+cm.mva01s[    ]+s0,s7
> +[       ]*[0-9a-f]+:[   ]+ac7a[         ]+cm.mva01s[    ]+s0,s6
> +[       ]*[0-9a-f]+:[   ]+acfe[         ]+cm.mva01s[    ]+s1,s7
> +[       ]*[0-9a-f]+:[   ]+acfa[         ]+cm.mva01s[    ]+s1,s6
> +[       ]*[0-9a-f]+:[   ]+afee[         ]+cm.mva01s[    ]+s7,s3
> +[       ]*[0-9a-f]+:[   ]+ade2[         ]+cm.mva01s[    ]+s3,s0
> +[       ]*[0-9a-f]+:[   ]+aef2[         ]+cm.mva01s[    ]+s5,s4
> +[       ]*[0-9a-f]+:[   ]+aefa[         ]+cm.mva01s[    ]+s5,s6
> +[       ]*[0-9a-f]+:[   ]+afa2[         ]+cm.mvsa01[    ]+s7,s0
> +[       ]*[0-9a-f]+:[   ]+af22[         ]+cm.mvsa01[    ]+s6,s0
> +[       ]*[0-9a-f]+:[   ]+afa6[         ]+cm.mvsa01[    ]+s7,s1
> +[       ]*[0-9a-f]+:[   ]+af26[         ]+cm.mvsa01[    ]+s6,s1
> +[       ]*[0-9a-f]+:[   ]+adbe[         ]+cm.mvsa01[    ]+s3,s7
> +[       ]*[0-9a-f]+:[   ]+ada2[         ]+cm.mvsa01[    ]+s3,s0
> +[       ]*[0-9a-f]+:[   ]+aeb2[         ]+cm.mvsa01[    ]+s5,s4
> +[       ]*[0-9a-f]+:[   ]+aeba[         ]+cm.mvsa01[    ]+s5,s6
> diff --git a/gas/testsuite/gas/riscv/zcmp-mv.s b/gas/testsuite/gas/riscv/zcmp-mv.s
> new file mode 100644
> index 00000000000..0bcf2a6cd98
> --- /dev/null
> +++ b/gas/testsuite/gas/riscv/zcmp-mv.s
> @@ -0,0 +1,21 @@
> +target:
> +
> +       # cm.mva01s
> +       cm.mva01s s0,s7
> +       cm.mva01s s0,s6
> +       cm.mva01s s1,s7
> +       cm.mva01s s1,s6
> +       cm.mva01s s7,s3
> +       cm.mva01s x19,s0
> +       cm.mva01s s5,x20
> +       cm.mva01s x21,x22
> +
> +       # cm.mvsa01
> +       cm.mvsa01 s7,s0
> +       cm.mvsa01 s6,s0
> +       cm.mvsa01 s7,s1
> +       cm.mvsa01 s6,s1
> +       cm.mvsa01 s3,s7
> +       cm.mvsa01 x19,s0
> +       cm.mvsa01 s5,x20
> +       cm.mvsa01 x21,x22
> diff --git a/include/opcode/riscv-opc.h b/include/opcode/riscv-opc.h
> index 9161dc371bc..8aeedf7f8f8 100644
> --- a/include/opcode/riscv-opc.h
> +++ b/include/opcode/riscv-opc.h
> @@ -2244,6 +2244,10 @@
>  #define MASK_CM_POPRET 0xff03
>  #define MATCH_CM_POPRETZ 0xbc02
>  #define MASK_CM_POPRETZ 0xff03
> +#define MATCH_CM_MVA01S 0xac62
> +#define MASK_CM_MVA01S 0xfc63
> +#define MATCH_CM_MVSA01 0xac22
> +#define MASK_CM_MVSA01 0xfc63
>  /* Svinval instruction.  */
>  #define MATCH_SINVAL_VMA 0x16000073
>  #define MASK_SINVAL_VMA 0xfe007fff
> @@ -3552,6 +3556,8 @@ DECLARE_INSN(cm_push, MATCH_CM_PUSH, MASK_CM_PUSH)
>  DECLARE_INSN(cm_pop, MATCH_CM_POP, MASK_CM_POP)
>  DECLARE_INSN(cm_popret, MATCH_CM_POPRET, MASK_CM_POPRET)
>  DECLARE_INSN(cm_popretz, MATCH_CM_POPRETZ, MASK_CM_POPRETZ)
> +DECLARE_INSN(cm_mvsa01, MATCH_CM_MVSA01, MASK_CM_MVSA01)
> +DECLARE_INSN(cm_mva01s, MATCH_CM_MVA01S, MASK_CM_MVA01S)
>  /* 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 7b1ed47aa5d..dead4fbdda1 100644
> --- a/include/opcode/riscv.h
> +++ b/include/opcode/riscv.h
> @@ -338,6 +338,10 @@ static inline unsigned int riscv_insn_length (insn_t insn)
>  #define OP_MASK_RLIST          0xf
>  #define OP_SH_RLIST            4
>  #define SP_ALIGNMENT 16
> +#define OP_MASK_SREG1          0x7
> +#define OP_SH_SREG1            7
> +#define OP_MASK_SREG2          0x7
> +#define OP_SH_SREG2            2
>
>  #define NVECR 32
>  #define NVECM 1
> @@ -353,7 +357,10 @@ static inline unsigned int riscv_insn_length (insn_t insn)
>  #define X_T2 7
>  #define X_S0 8
>  #define X_S1 9
> +#define X_A0 10
> +#define X_A1 11
>  #define X_S2 18
> +#define X_S7 23
>  #define X_S10 26
>  #define X_S11 27
>  #define X_T3 28
> @@ -401,6 +408,11 @@ static inline unsigned int riscv_insn_length (insn_t insn)
>  /* The maximal number of subset can be required.  */
>  #define MAX_SUBSET_NUM 4
>
> +/* The range of sregs.  */
> +#define RISCV_SREG_0_7(REGNO) \
> +       ((REGNO == X_S0 || REGNO == X_S1) \
> +        || (REGNO >= X_S2 && REGNO <= X_S7))
> +
>  /* All RISC-V instructions belong to at least one of these classes.  */
>  enum riscv_insn_class
>  {
> diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c
> index 810b24f5796..26914d8950c 100644
> --- a/opcodes/riscv-dis.c
> +++ b/opcodes/riscv-dis.c
> @@ -263,6 +263,17 @@ riscv_get_spimm (insn_t l)
>    return spimm;
>  }
>
> +/* Get s-register regno by using sreg number.
> +   e.g. the regno of s0 is 8, so
> +   riscv_zcmp_get_sregno (0) equals 8. */
> +
> +static unsigned
> +riscv_zcmp_get_sregno (unsigned sreg_idx)
> +{
> +  return sreg_idx > 1 ?
> +      sreg_idx + 16 : sreg_idx + 8;
> +}
> +
>  /* Print insn arguments for 32/64-bit code.  */
>
>  static void
> @@ -666,6 +677,14 @@ print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
>             case 'c': /* Zcb extension 16 bits length instruction fields. */
>               switch (*++oparg)
>                 {
> +               case '1':
> +                   print (info->stream, dis_style_register, "%s",
> +                     riscv_gpr_names[riscv_zcmp_get_sregno (EXTRACT_OPERAND (SREG1, l))]);
> +                   break;
> +               case '2':
> +                   print (info->stream, dis_style_register, "%s",
> +                     riscv_gpr_names[riscv_zcmp_get_sregno (EXTRACT_OPERAND (SREG2, l))]);
> +                   break;
>                 case 'b':
>                   print (info->stream, dis_style_immediate, "%d",
>                          (int)EXTRACT_ZCB_BYTE_UIMM (l));
> diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
> index fe700dbd609..f5e736193b3 100644
> --- a/opcodes/riscv-opc.c
> +++ b/opcodes/riscv-opc.c
> @@ -321,6 +321,13 @@ match_th_load_pair(const struct riscv_opcode *op,
>    return rd1 != rd2 && rd1 != rs && rd2 != rs && match_opcode (op, insn);
>  }
>
> +static int
> +match_sreg1_not_eq_sreg2 (const struct riscv_opcode *op, insn_t insn)
> +{
> +  return match_opcode (op, insn)
> +      && (EXTRACT_OPERAND (SREG1, insn) != EXTRACT_OPERAND (SREG2, insn));
> +}
> +
>  /* The order of overloaded instructions matters.  Label arguments and
>     register arguments look the same. Instructions that can have either
>     for arguments must apear in the correct order in this table for the
> @@ -1986,6 +1993,8 @@ const struct riscv_opcode riscv_opcodes[] =
>  {"cm.pop",     0,  INSN_CLASS_ZCMP, "{Wcr},Wcp",  MATCH_CM_POP, MASK_CM_POP, match_opcode, 0 },
>  {"cm.popret",  0,  INSN_CLASS_ZCMP, "{Wcr},Wcp",  MATCH_CM_POPRET, MASK_CM_POPRET, match_opcode, 0 },
>  {"cm.popretz", 0,  INSN_CLASS_ZCMP, "{Wcr},Wcp",  MATCH_CM_POPRETZ, MASK_CM_POPRETZ, match_opcode, 0 },
> +{"cm.mva01s",  0,  INSN_CLASS_ZCMP, "Wc1,Wc2",    MATCH_CM_MVA01S, MASK_CM_MVA01S, match_opcode, 0 },
> +{"cm.mvsa01",  0,  INSN_CLASS_ZCMP, "Wc1,Wc2",    MATCH_CM_MVSA01, MASK_CM_MVSA01, match_sreg1_not_eq_sreg2, 0 },
>
>  /* Supervisor instructions.  */
>  {"csrr",       0, INSN_CLASS_ZICSR, "d,E",   MATCH_CSRRS, MASK_CSRRS|MASK_RS1, match_opcode, INSN_ALIAS },
> --
> 2.25.1
>

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

end of thread, other threads:[~2024-01-05  7:02 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-20  7:06 [PATCH v3 1/2] RISC-V: Support Zcmp push/pop instructions Jiawei
2023-11-20  7:06 ` [PATCH v3 2/2] RISC-V: Support Zcmp cm.mv instructions Jiawei
2024-01-05  7:02   ` Kito Cheng
2024-01-05  7:01 ` [PATCH v3 1/2] RISC-V: Support Zcmp push/pop instructions Kito Cheng

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