[10/19] RISC-V: check operands for Zdinx in RV32

Message ID caa092f8-7262-44b3-b139-7478ba5fb2db@suse.com
State New
Headers
Series RISC-V: assorted fixes and (hopefully) improvements |

Commit Message

Jan Beulich April 21, 2026, 11:53 a.m. UTC
  Like for Zilsd, register pair operands are required to be encoded with the
low bit clear. Since match functions don't have XLEN available, introduce
respective flags, to be used explicitly in assembler and disassembler.
---
What about Zqinx? There's no formal spec for that extension afaics. For
RV64, does that behave like Zdinx for RV32? For RV32, does it require
register numbers to be divisible by 4? Or is it not a thing there in the
first place?

No similar checking is easily possible for scalar vector insns, as element
width there isn't encoded in the insn. (Plus V and Zdinx can't be enabled
together right now anyway.)

We could further use RV32_EVEN_CHECK for Zilsd as well, then allowing to
eliminate one or two match functions. (Hence RV32_EVEN_CHECK()'s scope is
pretty wide.)
  

Patch

--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -2908,6 +2908,12 @@  riscv_ip (char *str, struct riscv_cl_ins
       *imm_reloc = BFD_RELOC_UNUSED;
       p = percent_op_null;
 
+#define RV32_EVEN_CHECK(form, rclass, regno)     \
+  (!(ip->insn_mo->pinfo & INSN_RV32_EVEN_##form) \
+   || xlen != 32                                 \
+   || (rclass) != RCLASS_GPR                     \
+   || !((regno) & 1))
+
       for (oparg = insn->args;; ++oparg)
 	{
 	  opargStart = oparg;
@@ -2915,6 +2921,8 @@  riscv_ip (char *str, struct riscv_cl_ins
 	    ++asarg;
 	  switch (*oparg)
 	    {
+	      enum reg_class rclass;
+
 	    case '\0': /* End of args.  */
 	      if (insn->match_func && !insn->match_func (insn, ip->insn_opcode))
 		break;
@@ -3554,32 +3562,40 @@  riscv_ip (char *str, struct riscv_cl_ins
 	    case 'T': /* Floating point RS2.  */
 	    case 'U': /* Floating point RS1 and RS2.  */
 	    case 'R': /* Floating point RS3.  */
-	      if (reg_lookup (&asarg,
-			      (riscv_subset_supports (&riscv_rps_as, "zfinx")
-			      ? RCLASS_GPR : RCLASS_FPR), &regno))
+	      rclass = riscv_subset_supports (&riscv_rps_as, "zfinx")
+		       ? RCLASS_GPR : RCLASS_FPR;
+	      if (reg_lookup (&asarg, rclass, &regno))
 		{
 		  char c = *oparg;
 		  if (is_whitespace (*asarg))
 		    ++asarg;
+
 		  switch (c)
 		    {
 		    case 'D':
+		      if (!RV32_EVEN_CHECK (D, rclass, regno))
+			break;
 		      INSERT_OPERAND (RD, *ip, regno);
-		      break;
+		      continue;
 		    case 'S':
+		      if (!RV32_EVEN_CHECK (S, rclass, regno))
+			break;
 		      INSERT_OPERAND (RS1, *ip, regno);
-		      break;
+		      continue;
 		    case 'U':
 		      INSERT_OPERAND (RS1, *ip, regno);
 		      /* Fall through.  */
 		    case 'T':
+		      if (!RV32_EVEN_CHECK (T, rclass, regno))
+			break;
 		      INSERT_OPERAND (RS2, *ip, regno);
-		      break;
+		      continue;
 		    case 'R':
+		      if (!RV32_EVEN_CHECK (R, rclass, regno))
+			break;
 		      INSERT_OPERAND (RS3, *ip, regno);
-		      break;
+		      continue;
 		    }
-		  continue;
 		}
 	      break;
 
@@ -4282,6 +4298,8 @@  riscv_ip (char *str, struct riscv_cl_ins
       insn_with_csr = false;
     }
 
+#undef RV32_EVEN_CHECK
+
  out:
   /* Restore the character we might have clobbered above.  */
   if (save_c)
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zdinx-rv32.d
@@ -0,0 +1,52 @@ 
+#as: -march=rv32i_zdinx
+#objdump: -dwr
+
+.*:[ 	]+file format .*
+
+Disassembly of section .text:
+
+0+000 <target>:
+[ 	]+[0-9a-f]+:[ 	]+02e67553[ 	]+fadd\.d[ 	]+a0,a2,a4
+[ 	]+[0-9a-f]+:[ 	]+02e60553[ 	]+fadd\.d[ 	]+a0,a2,a4,rne
+[ 	]+[0-9a-f]+:[ 	]+0ae67553[ 	]+fsub\.d[ 	]+a0,a2,a4
+[ 	]+[0-9a-f]+:[ 	]+0ae60553[ 	]+fsub\.d[ 	]+a0,a2,a4,rne
+[ 	]+[0-9a-f]+:[ 	]+12e67553[ 	]+fmul\.d[ 	]+a0,a2,a4
+[ 	]+[0-9a-f]+:[ 	]+12e60553[ 	]+fmul\.d[ 	]+a0,a2,a4,rne
+[ 	]+[0-9a-f]+:[ 	]+1ae67553[ 	]+fdiv\.d[ 	]+a0,a2,a4
+[ 	]+[0-9a-f]+:[ 	]+1ae60553[ 	]+fdiv\.d[ 	]+a0,a2,a4,rne
+[ 	]+[0-9a-f]+:[ 	]+5a067553[ 	]+fsqrt\.d[ 	]+a0,a2
+[ 	]+[0-9a-f]+:[ 	]+5a060553[ 	]+fsqrt\.d[ 	]+a0,a2,rne
+[ 	]+[0-9a-f]+:[ 	]+2ae60553[ 	]+fmin\.d[ 	]+a0,a2,a4
+[ 	]+[0-9a-f]+:[ 	]+2ae61553[ 	]+fmax\.d[ 	]+a0,a2,a4
+[ 	]+[0-9a-f]+:[ 	]+82e67543[ 	]+fmadd\.d[ 	]+a0,a2,a4,a6
+[ 	]+[0-9a-f]+:[ 	]+82e60543[ 	]+fmadd\.d[ 	]+a0,a2,a4,a6,rne
+[ 	]+[0-9a-f]+:[ 	]+82e6754f[ 	]+fnmadd\.d[ 	]+a0,a2,a4,a6
+[ 	]+[0-9a-f]+:[ 	]+82e6054f[ 	]+fnmadd\.d[ 	]+a0,a2,a4,a6,rne
+[ 	]+[0-9a-f]+:[ 	]+82e67547[ 	]+fmsub\.d[ 	]+a0,a2,a4,a6
+[ 	]+[0-9a-f]+:[ 	]+82e60547[ 	]+fmsub\.d[ 	]+a0,a2,a4,a6,rne
+[ 	]+[0-9a-f]+:[ 	]+82e6754b[ 	]+fnmsub\.d[ 	]+a0,a2,a4,a6
+[ 	]+[0-9a-f]+:[ 	]+82e6054b[ 	]+fnmsub\.d[ 	]+a0,a2,a4,a6,rne
+[ 	]+[0-9a-f]+:[ 	]+c20675d3[ 	]+fcvt\.w\.d[ 	]+a1,a2
+[ 	]+[0-9a-f]+:[ 	]+c20605d3[ 	]+fcvt\.w\.d[ 	]+a1,a2,rne
+[ 	]+[0-9a-f]+:[ 	]+c21675d3[ 	]+fcvt\.wu\.d[ 	]+a1,a2
+[ 	]+[0-9a-f]+:[ 	]+c21605d3[ 	]+fcvt\.wu\.d[ 	]+a1,a2,rne
+[ 	]+[0-9a-f]+:[ 	]+d2058553[ 	]+fcvt\.d\.w[ 	]+a0,a1
+[ 	]+[0-9a-f]+:[ 	]+d2158553[ 	]+fcvt\.d\.wu[ 	]+a0,a1
+[ 	]+[0-9a-f]+:[ 	]+42058553[ 	]+fcvt\.d\.s[ 	]+a0,a1
+[ 	]+[0-9a-f]+:[ 	]+401675d3[ 	]+fcvt\.s\.d[ 	]+a1,a2
+[ 	]+[0-9a-f]+:[ 	]+401605d3[ 	]+fcvt\.s\.d[ 	]+a1,a2,rne
+[ 	]+[0-9a-f]+:[ 	]+42258553[ 	]+fcvt\.d\.h[ 	]+a0,a1
+[ 	]+[0-9a-f]+:[ 	]+441675d3[ 	]+fcvt\.h\.d[ 	]+a1,a2
+[ 	]+[0-9a-f]+:[ 	]+441605d3[ 	]+fcvt\.h\.d[ 	]+a1,a2,rne
+[ 	]+[0-9a-f]+:[ 	]+22e60553[ 	]+fsgnj\.d[ 	]+a0,a2,a4
+[ 	]+[0-9a-f]+:[ 	]+22e61553[ 	]+fsgnjn\.d[ 	]+a0,a2,a4
+[ 	]+[0-9a-f]+:[ 	]+22e62553[ 	]+fsgnjx\.d[ 	]+a0,a2,a4
+[ 	]+[0-9a-f]+:[ 	]+a2e625d3[ 	]+feq\.d[ 	]+a1,a2,a4
+[ 	]+[0-9a-f]+:[ 	]+a2e615d3[ 	]+flt\.d[ 	]+a1,a2,a4
+[ 	]+[0-9a-f]+:[ 	]+a2e605d3[ 	]+fle\.d[ 	]+a1,a2,a4
+[ 	]+[0-9a-f]+:[ 	]+a2c715d3[ 	]+flt\.d[ 	]+a1,a4,a2
+[ 	]+[0-9a-f]+:[ 	]+a2c705d3[ 	]+fle\.d[ 	]+a1,a4,a2
+[ 	]+[0-9a-f]+:[ 	]+22c60553[ 	]+fmv\.d[ 	]+a0,a2
+[ 	]+[0-9a-f]+:[ 	]+22c61553[ 	]+fneg\.d[ 	]+a0,a2
+[ 	]+[0-9a-f]+:[ 	]+22c62553[ 	]+fabs\.d[ 	]+a0,a2
+[ 	]+[0-9a-f]+:[ 	]+e20615d3[ 	]+fclass\.d[ 	]+a1,a2
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zdinx-rv32.s
@@ -0,0 +1,52 @@ 
+target:
+	fadd.d		a0, a2, a4
+	fadd.d		a0, a2, a4, rne
+	fsub.d		a0, a2, a4
+	fsub.d		a0, a2, a4, rne
+	fmul.d		a0, a2, a4
+	fmul.d		a0, a2, a4, rne
+	fdiv.d		a0, a2, a4
+	fdiv.d		a0, a2, a4, rne
+	fsqrt.d		a0, a2
+	fsqrt.d		a0, a2, rne
+	fmin.d		a0, a2, a4
+	fmax.d		a0, a2, a4
+	fmadd.d		a0, a2, a4, a6
+	fmadd.d		a0, a2, a4, a6, rne
+	fnmadd.d	a0, a2, a4, a6
+	fnmadd.d	a0, a2, a4, a6, rne
+	fmsub.d		a0, a2, a4, a6
+	fmsub.d		a0, a2, a4, a6, rne
+	fnmsub.d	a0, a2, a4, a6
+	fnmsub.d	a0, a2, a4, a6, rne
+
+	fcvt.w.d	a1, a2
+	fcvt.w.d	a1, a2, rne
+	fcvt.wu.d	a1, a2
+	fcvt.wu.d	a1, a2, rne
+	fcvt.d.w	a0, a1
+	fcvt.d.wu	a0, a1
+
+	fcvt.d.s	a0, a1
+	fcvt.s.d	a1, a2
+	fcvt.s.d	a1, a2, rne
+
+	.option push
+	.option arch, +zhinxmin
+	fcvt.d.h	a0, a1
+	fcvt.h.d	a1, a2
+	fcvt.h.d	a1, a2, rne
+	.option pop
+
+	fsgnj.d		a0, a2, a4
+	fsgnjn.d	a0, a2, a4
+	fsgnjx.d	a0, a2, a4
+	feq.d		a1, a2, a4
+	flt.d		a1, a2, a4
+	fle.d		a1, a2, a4
+	fgt.d		a1, a2, a4
+	fge.d		a1, a2, a4
+	fmv.d		a0, a2
+	fneg.d		a0, a2
+	fabs.d		a0, a2
+	fclass.d	a1, a2
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zdinx-rv32-fail.d
@@ -0,0 +1,3 @@ 
+#as: -march=rv32i_zdinx
+#source: zdinx.s
+#error_output: zdinx-rv32-fail.l
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zdinx-rv32-fail.l
@@ -0,0 +1,47 @@ 
+.*: Assembler messages:
+.*: Error: illegal operands `fadd\.d .*'
+.*: Error: illegal operands `fadd\.d .*'
+.*: Error: illegal operands `fsub\.d .*'
+.*: Error: illegal operands `fsub\.d .*'
+.*: Error: illegal operands `fmul\.d .*'
+.*: Error: illegal operands `fmul\.d .*'
+.*: Error: illegal operands `fdiv\.d .*'
+.*: Error: illegal operands `fdiv\.d .*'
+.*: Error: illegal operands `fsqrt\.d .*'
+.*: Error: illegal operands `fsqrt\.d .*'
+.*: Error: illegal operands `fmin\.d .*'
+.*: Error: illegal operands `fmax\.d .*'
+.*: Error: illegal operands `fmadd\.d .*'
+.*: Error: illegal operands `fmadd\.d .*'
+.*: Error: illegal operands `fnmadd\.d .*'
+.*: Error: illegal operands `fnmadd\.d .*'
+.*: Error: illegal operands `fmsub\.d .*'
+.*: Error: illegal operands `fmsub\.d .*'
+.*: Error: illegal operands `fnmsub\.d .*'
+.*: Error: illegal operands `fnmsub\.d .*'
+.*: Error: illegal operands `fcvt\.w\.d .*'
+.*: Error: illegal operands `fcvt\.w\.d .*'
+.*: Error: illegal operands `fcvt\.wu\.d .*'
+.*: Error: illegal operands `fcvt\.wu\.d .*'
+.*: Error: unrecognized opcode `fcvt\.l\.d .*'
+.*: Error: unrecognized opcode `fcvt\.l\.d .*'
+.*: Error: unrecognized opcode `fcvt\.lu\.d .*'
+.*: Error: unrecognized opcode `fcvt\.lu\.d .*'
+.*: Error: unrecognized opcode `fcvt\.d\.l .*'
+.*: Error: unrecognized opcode `fcvt\.d\.l .*'
+.*: Error: unrecognized opcode `fcvt\.d\.lu .*'
+.*: Error: unrecognized opcode `fcvt\.d\.lu .*'
+.*: Error: illegal operands `fcvt\.s\.d .*'
+.*: Error: illegal operands `fcvt\.s\.d .*'
+.*: Error: illegal operands `fsgnj\.d .*'
+.*: Error: illegal operands `fsgnjn\.d .*'
+.*: Error: illegal operands `fsgnjx\.d .*'
+.*: Error: illegal operands `feq\.d .*'
+.*: Error: illegal operands `flt\.d .*'
+.*: Error: illegal operands `fle\.d .*'
+.*: Error: illegal operands `fgt\.d .*'
+.*: Error: illegal operands `fge\.d .*'
+.*: Error: illegal operands `fmv\.d .*'
+.*: Error: illegal operands `fneg\.d .*'
+.*: Error: illegal operands `fabs\.d .*'
+.*: Error: illegal operands `fclass\.d .*'
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -679,6 +679,18 @@  struct riscv_opcode
 #define INSN_8_BYTE		0x00000040
 #define INSN_16_BYTE		0x00000050
 
+/* Operands required to be an even-numbered register (pair) in RV32.  */
+#define INSN_RV32_EVEN_D	0x00000100
+#define INSN_RV32_EVEN_S	0x00000200
+#define INSN_RV32_EVEN_T	0x00000400 /* Also covering U.  */
+#define INSN_RV32_EVEN_R	0x00000800
+/* Shorthands for combinations of the above.  */
+#define INSN_RV32_EVEN_DS	(INSN_RV32_EVEN_D   | INSN_RV32_EVEN_S)
+#define INSN_RV32_EVEN_DST	(INSN_RV32_EVEN_DS  | INSN_RV32_EVEN_T)
+#define INSN_RV32_EVEN_DSTR	(INSN_RV32_EVEN_DST | INSN_RV32_EVEN_R)
+#define INSN_RV32_EVEN_DU	(INSN_RV32_EVEN_D   | INSN_RV32_EVEN_T)
+#define INSN_RV32_EVEN_ST	(INSN_RV32_EVEN_S   | INSN_RV32_EVEN_T)
+
 /* Instruction is actually a macro.  It should be ignored by the
    disassembler, and requires special treatment by the assembler.  */
 #define INSN_MACRO		0xffffffff
--- a/opcodes/riscv-dis.c
+++ b/opcodes/riscv-dis.c
@@ -1032,6 +1032,23 @@  riscv_disassemble_insn (bfd_vma memaddr,
  	      if ((op->pinfo & INSN_V_EEW64)
 		  && !riscv_subset_supports (&pd->riscv_rps_dis, "zve64x"))
 		continue;
+
+	      if (pd->xlen == 32
+		  && riscv_subset_supports (&pd->riscv_rps_dis, "zdinx"))
+		{
+		  if ((op->pinfo & INSN_RV32_EVEN_D)
+		      && (word & (1u << OP_SH_RD)))
+		    continue;
+		  if ((op->pinfo & INSN_RV32_EVEN_S)
+		      && (word & (1u << OP_SH_RS1)))
+		    continue;
+		  if ((op->pinfo & INSN_RV32_EVEN_T)
+		      && (word & (1u << OP_SH_RS2)))
+		    continue;
+		  if ((op->pinfo & INSN_RV32_EVEN_R)
+		      && (word & (1u << OP_SH_RS3)))
+		    continue;
+		}
 	    }
 
 	  /* It's a match.  */
--- a/opcodes/riscv-opc.c
+++ b/opcodes/riscv-opc.c
@@ -931,10 +931,10 @@  const struct riscv_opcode riscv_opcodes[
 {"fcvt.h.w",   0, INSN_CLASS_ZFH_INX,  "D,sm",      MATCH_FCVT_H_W, MASK_FCVT_H_W, match_opcode, 0 },
 {"fcvt.h.wu",  0, INSN_CLASS_ZFH_INX,  "D,sm",      MATCH_FCVT_H_WU, MASK_FCVT_H_WU, match_opcode, 0 },
 {"fcvt.s.h",   0, INSN_CLASS_ZFHMIN_INX, "D,S",     MATCH_FCVT_S_H, MASK_FCVT_S_H|MASK_RM, match_opcode, 0 },
-{"fcvt.d.h",   0, INSN_CLASS_ZFHMIN_AND_D_INX,     "D,S",       MATCH_FCVT_D_H, MASK_FCVT_D_H|MASK_RM, match_opcode, 0 },
-{"fcvt.q.h",   0, INSN_CLASS_ZFHMIN_AND_Q_INX,     "D,S",       MATCH_FCVT_Q_H, MASK_FCVT_Q_H|MASK_RM, match_opcode, 0 },
+{"fcvt.d.h",   0, INSN_CLASS_ZFHMIN_AND_D_INX, "D,S", MATCH_FCVT_D_H, MASK_FCVT_D_H|MASK_RM, match_opcode, INSN_RV32_EVEN_D },
+{"fcvt.q.h",   0, INSN_CLASS_ZFHMIN_AND_Q_INX, "D,S", MATCH_FCVT_Q_H, MASK_FCVT_Q_H|MASK_RM, match_opcode, 0 },
 {"fcvt.h.s",   0, INSN_CLASS_ZFHMIN_INX, "D,Sm",    MATCH_FCVT_H_S, MASK_FCVT_H_S, match_opcode, 0 },
-{"fcvt.h.d",   0, INSN_CLASS_ZFHMIN_AND_D_INX, "D,Sm", MATCH_FCVT_H_D, MASK_FCVT_H_D, match_opcode, 0 },
+{"fcvt.h.d",   0, INSN_CLASS_ZFHMIN_AND_D_INX, "D,Sm", MATCH_FCVT_H_D, MASK_FCVT_H_D, match_opcode, INSN_RV32_EVEN_S },
 {"fcvt.h.q",   0, INSN_CLASS_ZFHMIN_AND_Q_INX, "D,Sm", MATCH_FCVT_H_Q, MASK_FCVT_H_Q, match_opcode, 0 },
 {"fclass.h",   0, INSN_CLASS_ZFH_INX,   "d,S",       MATCH_FCLASS_H, MASK_FCLASS_H, match_opcode, 0 },
 {"feq.h",      0, INSN_CLASS_ZFH_INX,   "d,S,T",     MATCH_FEQ_H, MASK_FEQ_H, match_opcode, 0 },
@@ -1021,35 +1021,35 @@  const struct riscv_opcode riscv_opcodes[
 {"fsd",        0, INSN_CLASS_ZCD, "CD,Cl(Cs)", MATCH_C_FSD, MASK_C_FSD, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
 {"fsd",        0, INSN_CLASS_D,   "T,q(s)",    MATCH_FSD, MASK_FSD, match_opcode, INSN_DREF|INSN_8_BYTE },
 {"fsd",        0, INSN_CLASS_D,   "T,A,s",     0, (int) M_Sx_FSx, match_rs1_nonzero, INSN_MACRO },
-{"fmv.d",      0, INSN_CLASS_D_INX,   "D,U",       MATCH_FSGNJ_D, MASK_FSGNJ_D, match_rs1_eq_rs2, INSN_ALIAS },
-{"fneg.d",     0, INSN_CLASS_D_INX,   "D,U",       MATCH_FSGNJN_D, MASK_FSGNJN_D, match_rs1_eq_rs2, INSN_ALIAS },
-{"fabs.d",     0, INSN_CLASS_D_INX,   "D,U",       MATCH_FSGNJX_D, MASK_FSGNJX_D, match_rs1_eq_rs2, INSN_ALIAS },
-{"fsgnj.d",    0, INSN_CLASS_D_INX,   "D,S,T",     MATCH_FSGNJ_D, MASK_FSGNJ_D, match_opcode, 0 },
-{"fsgnjn.d",   0, INSN_CLASS_D_INX,   "D,S,T",     MATCH_FSGNJN_D, MASK_FSGNJN_D, match_opcode, 0 },
-{"fsgnjx.d",   0, INSN_CLASS_D_INX,   "D,S,T",     MATCH_FSGNJX_D, MASK_FSGNJX_D, match_opcode, 0 },
-{"fadd.d",     0, INSN_CLASS_D_INX,   "D,S,Tm",    MATCH_FADD_D, MASK_FADD_D, match_opcode, 0 },
-{"fsub.d",     0, INSN_CLASS_D_INX,   "D,S,Tm",    MATCH_FSUB_D, MASK_FSUB_D, match_opcode, 0 },
-{"fmul.d",     0, INSN_CLASS_D_INX,   "D,S,Tm",    MATCH_FMUL_D, MASK_FMUL_D, match_opcode, 0 },
-{"fdiv.d",     0, INSN_CLASS_D_INX,   "D,S,Tm",    MATCH_FDIV_D, MASK_FDIV_D, match_opcode, 0 },
-{"fsqrt.d",    0, INSN_CLASS_D_INX,   "D,Sm",      MATCH_FSQRT_D, MASK_FSQRT_D, match_opcode, 0 },
-{"fmin.d",     0, INSN_CLASS_D_INX,   "D,S,T",     MATCH_FMIN_D, MASK_FMIN_D, match_opcode, 0 },
-{"fmax.d",     0, INSN_CLASS_D_INX,   "D,S,T",     MATCH_FMAX_D, MASK_FMAX_D, match_opcode, 0 },
-{"fmadd.d",    0, INSN_CLASS_D_INX,   "D,S,T,Rm",  MATCH_FMADD_D, MASK_FMADD_D, match_opcode, 0 },
-{"fnmadd.d",   0, INSN_CLASS_D_INX,   "D,S,T,Rm",  MATCH_FNMADD_D, MASK_FNMADD_D, match_opcode, 0 },
-{"fmsub.d",    0, INSN_CLASS_D_INX,   "D,S,T,Rm",  MATCH_FMSUB_D, MASK_FMSUB_D, match_opcode, 0 },
-{"fnmsub.d",   0, INSN_CLASS_D_INX,   "D,S,T,Rm",  MATCH_FNMSUB_D, MASK_FNMSUB_D, match_opcode, 0 },
-{"fcvt.w.d",   0, INSN_CLASS_D_INX,   "d,Sm",      MATCH_FCVT_W_D, MASK_FCVT_W_D, match_opcode, 0 },
-{"fcvt.wu.d",  0, INSN_CLASS_D_INX,   "d,Sm",      MATCH_FCVT_WU_D, MASK_FCVT_WU_D, match_opcode, 0 },
-{"fcvt.d.w",   0, INSN_CLASS_D_INX,   "D,s",       MATCH_FCVT_D_W, MASK_FCVT_D_W|MASK_RM, match_opcode, 0 },
-{"fcvt.d.wu",  0, INSN_CLASS_D_INX,   "D,s",       MATCH_FCVT_D_WU, MASK_FCVT_D_WU|MASK_RM, match_opcode, 0 },
-{"fcvt.d.s",   0, INSN_CLASS_D_INX,   "D,S",       MATCH_FCVT_D_S, MASK_FCVT_D_S|MASK_RM, match_opcode, 0 },
-{"fcvt.s.d",   0, INSN_CLASS_D_INX,   "D,Sm",      MATCH_FCVT_S_D, MASK_FCVT_S_D, match_opcode, 0 },
-{"fclass.d",   0, INSN_CLASS_D_INX,   "d,S",       MATCH_FCLASS_D, MASK_FCLASS_D, match_opcode, 0 },
-{"feq.d",      0, INSN_CLASS_D_INX,   "d,S,T",     MATCH_FEQ_D, MASK_FEQ_D, match_opcode, 0 },
-{"flt.d",      0, INSN_CLASS_D_INX,   "d,S,T",     MATCH_FLT_D, MASK_FLT_D, match_opcode, 0 },
-{"fle.d",      0, INSN_CLASS_D_INX,   "d,S,T",     MATCH_FLE_D, MASK_FLE_D, match_opcode, 0 },
-{"fgt.d",      0, INSN_CLASS_D_INX,   "d,T,S",     MATCH_FLT_D, MASK_FLT_D, match_opcode, INSN_ALIAS },
-{"fge.d",      0, INSN_CLASS_D_INX,   "d,T,S",     MATCH_FLE_D, MASK_FLE_D, match_opcode, INSN_ALIAS },
+{"fmv.d",      0, INSN_CLASS_D_INX,   "D,U",       MATCH_FSGNJ_D, MASK_FSGNJ_D, match_rs1_eq_rs2, INSN_ALIAS|INSN_RV32_EVEN_DU },
+{"fneg.d",     0, INSN_CLASS_D_INX,   "D,U",       MATCH_FSGNJN_D, MASK_FSGNJN_D, match_rs1_eq_rs2, INSN_ALIAS|INSN_RV32_EVEN_DU },
+{"fabs.d",     0, INSN_CLASS_D_INX,   "D,U",       MATCH_FSGNJX_D, MASK_FSGNJX_D, match_rs1_eq_rs2, INSN_ALIAS|INSN_RV32_EVEN_DU },
+{"fsgnj.d",    0, INSN_CLASS_D_INX,   "D,S,T",     MATCH_FSGNJ_D, MASK_FSGNJ_D, match_opcode, INSN_RV32_EVEN_DST },
+{"fsgnjn.d",   0, INSN_CLASS_D_INX,   "D,S,T",     MATCH_FSGNJN_D, MASK_FSGNJN_D, match_opcode, INSN_RV32_EVEN_DST },
+{"fsgnjx.d",   0, INSN_CLASS_D_INX,   "D,S,T",     MATCH_FSGNJX_D, MASK_FSGNJX_D, match_opcode, INSN_RV32_EVEN_DST },
+{"fadd.d",     0, INSN_CLASS_D_INX,   "D,S,Tm",    MATCH_FADD_D, MASK_FADD_D, match_opcode, INSN_RV32_EVEN_DST },
+{"fsub.d",     0, INSN_CLASS_D_INX,   "D,S,Tm",    MATCH_FSUB_D, MASK_FSUB_D, match_opcode, INSN_RV32_EVEN_DST },
+{"fmul.d",     0, INSN_CLASS_D_INX,   "D,S,Tm",    MATCH_FMUL_D, MASK_FMUL_D, match_opcode, INSN_RV32_EVEN_DST },
+{"fdiv.d",     0, INSN_CLASS_D_INX,   "D,S,Tm",    MATCH_FDIV_D, MASK_FDIV_D, match_opcode, INSN_RV32_EVEN_DST },
+{"fsqrt.d",    0, INSN_CLASS_D_INX,   "D,Sm",      MATCH_FSQRT_D, MASK_FSQRT_D, match_opcode, INSN_RV32_EVEN_DS },
+{"fmin.d",     0, INSN_CLASS_D_INX,   "D,S,T",     MATCH_FMIN_D, MASK_FMIN_D, match_opcode, INSN_RV32_EVEN_DST },
+{"fmax.d",     0, INSN_CLASS_D_INX,   "D,S,T",     MATCH_FMAX_D, MASK_FMAX_D, match_opcode, INSN_RV32_EVEN_DST },
+{"fmadd.d",    0, INSN_CLASS_D_INX,   "D,S,T,Rm",  MATCH_FMADD_D, MASK_FMADD_D, match_opcode, INSN_RV32_EVEN_DSTR },
+{"fnmadd.d",   0, INSN_CLASS_D_INX,   "D,S,T,Rm",  MATCH_FNMADD_D, MASK_FNMADD_D, match_opcode, INSN_RV32_EVEN_DSTR },
+{"fmsub.d",    0, INSN_CLASS_D_INX,   "D,S,T,Rm",  MATCH_FMSUB_D, MASK_FMSUB_D, match_opcode, INSN_RV32_EVEN_DSTR },
+{"fnmsub.d",   0, INSN_CLASS_D_INX,   "D,S,T,Rm",  MATCH_FNMSUB_D, MASK_FNMSUB_D, match_opcode, INSN_RV32_EVEN_DSTR },
+{"fcvt.w.d",   0, INSN_CLASS_D_INX,   "d,Sm",      MATCH_FCVT_W_D, MASK_FCVT_W_D, match_opcode, INSN_RV32_EVEN_S },
+{"fcvt.wu.d",  0, INSN_CLASS_D_INX,   "d,Sm",      MATCH_FCVT_WU_D, MASK_FCVT_WU_D, match_opcode, INSN_RV32_EVEN_S },
+{"fcvt.d.w",   0, INSN_CLASS_D_INX,   "D,s",       MATCH_FCVT_D_W, MASK_FCVT_D_W|MASK_RM, match_opcode, INSN_RV32_EVEN_D },
+{"fcvt.d.wu",  0, INSN_CLASS_D_INX,   "D,s",       MATCH_FCVT_D_WU, MASK_FCVT_D_WU|MASK_RM, match_opcode, INSN_RV32_EVEN_D },
+{"fcvt.d.s",   0, INSN_CLASS_D_INX,   "D,S",       MATCH_FCVT_D_S, MASK_FCVT_D_S|MASK_RM, match_opcode, INSN_RV32_EVEN_D },
+{"fcvt.s.d",   0, INSN_CLASS_D_INX,   "D,Sm",      MATCH_FCVT_S_D, MASK_FCVT_S_D, match_opcode, INSN_RV32_EVEN_S },
+{"fclass.d",   0, INSN_CLASS_D_INX,   "d,S",       MATCH_FCLASS_D, MASK_FCLASS_D, match_opcode, INSN_RV32_EVEN_S },
+{"feq.d",      0, INSN_CLASS_D_INX,   "d,S,T",     MATCH_FEQ_D, MASK_FEQ_D, match_opcode, INSN_RV32_EVEN_ST },
+{"flt.d",      0, INSN_CLASS_D_INX,   "d,S,T",     MATCH_FLT_D, MASK_FLT_D, match_opcode, INSN_RV32_EVEN_ST },
+{"fle.d",      0, INSN_CLASS_D_INX,   "d,S,T",     MATCH_FLE_D, MASK_FLE_D, match_opcode, INSN_RV32_EVEN_ST },
+{"fgt.d",      0, INSN_CLASS_D_INX,   "d,T,S",     MATCH_FLT_D, MASK_FLT_D, match_opcode, INSN_ALIAS|INSN_RV32_EVEN_ST },
+{"fge.d",      0, INSN_CLASS_D_INX,   "d,T,S",     MATCH_FLE_D, MASK_FLE_D, match_opcode, INSN_ALIAS|INSN_RV32_EVEN_ST },
 {"fmv.x.d",   64, INSN_CLASS_D,   "d,S",       MATCH_FMV_X_D, MASK_FMV_X_D, match_opcode, 0 },
 {"fmv.d.x",   64, INSN_CLASS_D,   "D,s",       MATCH_FMV_D_X, MASK_FMV_D_X, match_opcode, 0 },
 {"fcvt.l.d",  64, INSN_CLASS_D_INX,   "d,Sm",      MATCH_FCVT_L_D, MASK_FCVT_L_D, match_opcode, 0 },