[v3,08/13] RISC-V: check operands for Zqinx

Message ID 1aecd2c8-d5f5-4f36-a8ae-36ae4ea83978@suse.com
State New
Headers
Series RISC-V: assorted fixes and (hopefully) improvements |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed

Commit Message

Jan Beulich June 19, 2026, 11:51 a.m. UTC
  By analogy to Zdinx on RV32, register pair operands are presumably (there
not being any formal spec afaict) required to be encoded with the low bit
clear in RV64; in RV32 the low two bits need to be clear. Since match
functions don't have XLEN available, introduce respective flags, to be
used explicitly in assembler and disassembler.
---
Does Zqinx actually make much sense in RV32? Function argument passing,
for example, ends up odd: a0..a3 aren't suitable to hold an argument.
Hence only a single argument can be passed in registers (a2..a5).

No similar checking is easily possible for scalar vector insns, as element
width there isn't encoded in the insn. (Plus V and Zqinx can't be enabled
together right now anyway.)
---
v3: Fix typo in comment.
v2: New.
  

Patch

--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -2934,6 +2934,12 @@  riscv_ip (char *str, struct riscv_cl_ins
    || (rclass) != RCLASS_GPR                     \
    || !((regno) & 1))
 
+#define RV64_EVEN_CHECK(form, rclass, regno)     \
+  (!(ip->insn_mo->pinfo & INSN_RV64_EVEN_##form) \
+   || xlen > 64                                  \
+   || (rclass) != RCLASS_GPR                     \
+   || !((regno) & (xlen == 32 ? 3 : 1)))
+
       for (oparg = insn->args;; ++oparg)
 	{
 	  opargStart = oparg;
@@ -3593,12 +3599,14 @@  riscv_ip (char *str, struct riscv_cl_ins
 		  switch (c)
 		    {
 		    case 'D':
-		      if (!RV32_EVEN_CHECK (D, rclass, regno))
+		      if (!RV32_EVEN_CHECK (D, rclass, regno)
+			  || !RV64_EVEN_CHECK (D, rclass, regno))
 			break;
 		      INSERT_OPERAND (RD, *ip, regno);
 		      continue;
 		    case 'S':
-		      if (!RV32_EVEN_CHECK (S, rclass, regno))
+		      if (!RV32_EVEN_CHECK (S, rclass, regno)
+			  || !RV64_EVEN_CHECK (S, rclass, regno))
 			break;
 		      INSERT_OPERAND (RS1, *ip, regno);
 		      continue;
@@ -3606,12 +3614,14 @@  riscv_ip (char *str, struct riscv_cl_ins
 		      INSERT_OPERAND (RS1, *ip, regno);
 		      /* Fall through.  */
 		    case 'T':
-		      if (!RV32_EVEN_CHECK (T, rclass, regno))
+		      if (!RV32_EVEN_CHECK (T, rclass, regno)
+			  || !RV64_EVEN_CHECK (T, rclass, regno))
 			break;
 		      INSERT_OPERAND (RS2, *ip, regno);
 		      continue;
 		    case 'R':
-		      if (!RV32_EVEN_CHECK (R, rclass, regno))
+		      if (!RV32_EVEN_CHECK (R, rclass, regno)
+			  || !RV64_EVEN_CHECK (R, rclass, regno))
 			break;
 		      INSERT_OPERAND (RS3, *ip, regno);
 		      continue;
@@ -4312,6 +4322,7 @@  riscv_ip (char *str, struct riscv_cl_ins
     }
 
 #undef RV32_EVEN_CHECK
+#undef RV64_EVEN_CHECK
 
  out:
   /* Restore the character we might have clobbered above.  */
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zqinx-rv32.d
@@ -0,0 +1,52 @@ 
+#as: -march=rv32i_zqinx
+#objdump: -dwr
+
+.*:[ 	]+file format .*
+
+Disassembly of section .text:
+
+0+000 <target>:
+[ 	]+[0-9a-f]+:[ 	]+078a7453[ 	]+fadd\.q[ 	]+s0,s4,s8
+[ 	]+[0-9a-f]+:[ 	]+078a0453[ 	]+fadd\.q[ 	]+s0,s4,s8,rne
+[ 	]+[0-9a-f]+:[ 	]+0f8a7453[ 	]+fsub\.q[ 	]+s0,s4,s8
+[ 	]+[0-9a-f]+:[ 	]+0f8a0453[ 	]+fsub\.q[ 	]+s0,s4,s8,rne
+[ 	]+[0-9a-f]+:[ 	]+178a7453[ 	]+fmul\.q[ 	]+s0,s4,s8
+[ 	]+[0-9a-f]+:[ 	]+178a0453[ 	]+fmul\.q[ 	]+s0,s4,s8,rne
+[ 	]+[0-9a-f]+:[ 	]+1f8a7453[ 	]+fdiv\.q[ 	]+s0,s4,s8
+[ 	]+[0-9a-f]+:[ 	]+1f8a0453[ 	]+fdiv\.q[ 	]+s0,s4,s8,rne
+[ 	]+[0-9a-f]+:[ 	]+5e0a7453[ 	]+fsqrt\.q[ 	]+s0,s4
+[ 	]+[0-9a-f]+:[ 	]+5e0a0453[ 	]+fsqrt\.q[ 	]+s0,s4,rne
+[ 	]+[0-9a-f]+:[ 	]+2f8a0453[ 	]+fmin\.q[ 	]+s0,s4,s8
+[ 	]+[0-9a-f]+:[ 	]+2f8a1453[ 	]+fmax\.q[ 	]+s0,s4,s8
+[ 	]+[0-9a-f]+:[ 	]+678a7443[ 	]+fmadd\.q[ 	]+s0,s4,s8,a2
+[ 	]+[0-9a-f]+:[ 	]+678a0443[ 	]+fmadd\.q[ 	]+s0,s4,s8,a2,rne
+[ 	]+[0-9a-f]+:[ 	]+678a744f[ 	]+fnmadd\.q[ 	]+s0,s4,s8,a2
+[ 	]+[0-9a-f]+:[ 	]+678a044f[ 	]+fnmadd\.q[ 	]+s0,s4,s8,a2,rne
+[ 	]+[0-9a-f]+:[ 	]+678a7447[ 	]+fmsub\.q[ 	]+s0,s4,s8,a2
+[ 	]+[0-9a-f]+:[ 	]+678a0447[ 	]+fmsub\.q[ 	]+s0,s4,s8,a2,rne
+[ 	]+[0-9a-f]+:[ 	]+678a744b[ 	]+fnmsub\.q[ 	]+s0,s4,s8,a2
+[ 	]+[0-9a-f]+:[ 	]+678a044b[ 	]+fnmsub\.q[ 	]+s0,s4,s8,a2,rne
+[ 	]+[0-9a-f]+:[ 	]+c60a75d3[ 	]+fcvt\.w\.q[ 	]+a1,s4
+[ 	]+[0-9a-f]+:[ 	]+c60a05d3[ 	]+fcvt\.w\.q[ 	]+a1,s4,rne
+[ 	]+[0-9a-f]+:[ 	]+c61a75d3[ 	]+fcvt\.wu\.q[ 	]+a1,s4
+[ 	]+[0-9a-f]+:[ 	]+c61a05d3[ 	]+fcvt\.wu\.q[ 	]+a1,s4,rne
+[ 	]+[0-9a-f]+:[ 	]+d6058453[ 	]+fcvt\.q\.w[ 	]+s0,a1
+[ 	]+[0-9a-f]+:[ 	]+d6158453[ 	]+fcvt\.q\.wu[ 	]+s0,a1
+[ 	]+[0-9a-f]+:[ 	]+46058453[ 	]+fcvt\.q\.s[ 	]+s0,a1
+[ 	]+[0-9a-f]+:[ 	]+403a75d3[ 	]+fcvt\.s\.q[ 	]+a1,s4
+[ 	]+[0-9a-f]+:[ 	]+403a05d3[ 	]+fcvt\.s\.q[ 	]+a1,s4,rne
+[ 	]+[0-9a-f]+:[ 	]+46150453[ 	]+fcvt\.q\.d[ 	]+s0,a0
+[ 	]+[0-9a-f]+:[ 	]+423a7553[ 	]+fcvt\.d\.q[ 	]+a0,s4
+[ 	]+[0-9a-f]+:[ 	]+423a0553[ 	]+fcvt\.d\.q[ 	]+a0,s4,rne
+[ 	]+[0-9a-f]+:[ 	]+46258453[ 	]+fcvt\.q\.h[ 	]+s0,a1
+[ 	]+[0-9a-f]+:[ 	]+443a75d3[ 	]+fcvt\.h\.q[ 	]+a1,s4
+[ 	]+[0-9a-f]+:[ 	]+443a05d3[ 	]+fcvt\.h\.q[ 	]+a1,s4,rne
+[ 	]+[0-9a-f]+:[ 	]+278a0453[ 	]+fsgnj\.q[ 	]+s0,s4,s8
+[ 	]+[0-9a-f]+:[ 	]+278a1453[ 	]+fsgnjn\.q[ 	]+s0,s4,s8
+[ 	]+[0-9a-f]+:[ 	]+278a2453[ 	]+fsgnjx\.q[ 	]+s0,s4,s8
+[ 	]+[0-9a-f]+:[ 	]+a78a25d3[ 	]+feq\.q[ 	]+a1,s4,s8
+[ 	]+[0-9a-f]+:[ 	]+a78a15d3[ 	]+flt\.q[ 	]+a1,s4,s8
+[ 	]+[0-9a-f]+:[ 	]+a78a05d3[ 	]+fle\.q[ 	]+a1,s4,s8
+[ 	]+[0-9a-f]+:[ 	]+a74c15d3[ 	]+flt\.q[ 	]+a1,s8,s4
+[ 	]+[0-9a-f]+:[ 	]+a74c05d3[ 	]+fle\.q[ 	]+a1,s8,s4
+[ 	]+[0-9a-f]+:[ 	]+274a0453[ 	]+fmv\.q[ 	]+s0,s4
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zqinx-rv32.s
@@ -0,0 +1,54 @@ 
+target:
+	fadd.q		s0, s4, s8
+	fadd.q		s0, s4, s8, rne
+	fsub.q		s0, s4, s8
+	fsub.q		s0, s4, s8, rne
+	fmul.q		s0, s4, s8
+	fmul.q		s0, s4, s8, rne
+	fdiv.q		s0, s4, s8
+	fdiv.q		s0, s4, s8, rne
+	fsqrt.q		s0, s4
+	fsqrt.q		s0, s4, rne
+	fmin.q		s0, s4, s8
+	fmax.q		s0, s4, s8
+	fmadd.q		s0, s4, s8, a2
+	fmadd.q		s0, s4, s8, a2, rne
+	fnmadd.q	s0, s4, s8, a2
+	fnmadd.q	s0, s4, s8, a2, rne
+	fmsub.q		s0, s4, s8, a2
+	fmsub.q		s0, s4, s8, a2, rne
+	fnmsub.q	s0, s4, s8, a2
+	fnmsub.q	s0, s4, s8, a2, rne
+
+	fcvt.w.q	a1, s4
+	fcvt.w.q	a1, s4, rne
+	fcvt.wu.q	a1, s4
+	fcvt.wu.q	a1, s4, rne
+	fcvt.q.w	s0, a1
+	fcvt.q.wu	s0, a1
+
+	fcvt.q.s	s0, a1
+	fcvt.s.q	a1, s4
+	fcvt.s.q	a1, s4, rne
+
+	fcvt.q.d	s0, a0
+	fcvt.d.q	a0, s4
+	fcvt.d.q	a0, s4, rne
+
+	.option push
+	.option arch, +zhinxmin
+	fcvt.q.h	s0, a1
+	fcvt.h.q	a1, s4
+	fcvt.h.q	a1, s4, rne
+	.option pop
+
+	fsgnj.q		s0, s4, s8
+	fsgnjn.q	s0, s4, s8
+	fsgnjx.q	s0, s4, s8
+	feq.q		a1, s4, s8
+	flt.q		a1, s4, s8
+	fle.q		a1, s4, s8
+	fgt.q		a1, s4, s8
+	fge.q		a1, s4, s8
+
+	fmv.q		s0, s4
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zqinx-rv32-fail.d
@@ -0,0 +1,3 @@ 
+#as: -march=rv32i_zqinx
+#source: zqinx.s
+#error_output: zqinx-rv32-fail.l
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zqinx-rv32-fail.l
@@ -0,0 +1,42 @@ 
+.*: Assembler messages:
+.*: Error: illegal operands `fadd\.q .*'
+.*: Error: illegal operands `fadd\.q .*'
+.*: Error: illegal operands `fsub\.q .*'
+.*: Error: illegal operands `fsub\.q .*'
+.*: Error: illegal operands `fmul\.q .*'
+.*: Error: illegal operands `fmul\.q .*'
+.*: Error: illegal operands `fdiv\.q .*'
+.*: Error: illegal operands `fdiv\.q .*'
+.*: Error: illegal operands `fsqrt\.q .*'
+.*: Error: illegal operands `fsqrt\.q .*'
+.*: Error: illegal operands `fmin\.q .*'
+.*: Error: illegal operands `fmax\.q .*'
+.*: Error: illegal operands `fmadd\.q .*'
+.*: Error: illegal operands `fmadd\.q .*'
+.*: Error: illegal operands `fnmadd\.q .*'
+.*: Error: illegal operands `fnmadd\.q .*'
+.*: Error: illegal operands `fmsub\.q .*'
+.*: Error: illegal operands `fmsub\.q .*'
+.*: Error: illegal operands `fnmsub\.q .*'
+.*: Error: illegal operands `fnmsub\.q .*'
+.*: Error: unrecognized opcode `fcvt\.l\.q .*'
+.*: Error: unrecognized opcode `fcvt\.l\.q .*'
+.*: Error: unrecognized opcode `fcvt\.lu\.q .*'
+.*: Error: unrecognized opcode `fcvt\.lu\.q .*'
+.*: Error: illegal operands `fcvt\.q\.w .*'
+.*: Error: illegal operands `fcvt\.q\.wu .*'
+.*: Error: unrecognized opcode `fcvt\.q\.l .*'
+.*: Error: unrecognized opcode `fcvt\.q\.lu .*'
+.*: Error: illegal operands `fcvt\.q\.s .*'
+.*: Error: illegal operands `fcvt\.q\.d .*'
+.*: Error: illegal operands `fsgnj\.q .*'
+.*: Error: illegal operands `fsgnjn\.q .*'
+.*: Error: illegal operands `fsgnjx\.q .*'
+.*: Error: illegal operands `feq\.q .*'
+.*: Error: illegal operands `flt\.q .*'
+.*: Error: illegal operands `fle\.q .*'
+.*: Error: illegal operands `fgt\.q .*'
+.*: Error: illegal operands `fge\.q .*'
+.*: Error: illegal operands `fmv\.q .*'
+.*: Error: illegal operands `fneg\.q .*'
+.*: Error: illegal operands `fabs\.q .*'
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zqinx-rv64-fail.d
@@ -0,0 +1,2 @@ 
+#as: -march=rv64i_zqinx
+#error_output: zqinx-rv64-fail.l
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zqinx-rv64-fail.l
@@ -0,0 +1,62 @@ 
+.*: Assembler messages:
+.*: Error: illegal operands `fadd\.q .*'
+.*: Error: illegal operands `fadd\.q .*'
+.*: Error: illegal operands `fadd\.q .*'
+.*: Error: illegal operands `fsub\.q .*'
+.*: Error: illegal operands `fsub\.q .*'
+.*: Error: illegal operands `fsub\.q .*'
+.*: Error: illegal operands `fmul\.q .*'
+.*: Error: illegal operands `fmul\.q .*'
+.*: Error: illegal operands `fmul\.q .*'
+.*: Error: illegal operands `fdiv\.q .*'
+.*: Error: illegal operands `fdiv\.q .*'
+.*: Error: illegal operands `fdiv\.q .*'
+.*: Error: illegal operands `fsqrt\.q .*'
+.*: Error: illegal operands `fsqrt\.q .*'
+.*: Error: illegal operands `fmin\.q .*'
+.*: Error: illegal operands `fmin\.q .*'
+.*: Error: illegal operands `fmin\.q .*'
+.*: Error: illegal operands `fmax\.q .*'
+.*: Error: illegal operands `fmax\.q .*'
+.*: Error: illegal operands `fmax\.q .*'
+.*: Error: illegal operands `fmadd\.q .*'
+.*: Error: illegal operands `fmadd\.q .*'
+.*: Error: illegal operands `fmadd\.q .*'
+.*: Error: illegal operands `fmadd\.q .*'
+.*: Error: illegal operands `fnmadd\.q .*'
+.*: Error: illegal operands `fnmadd\.q .*'
+.*: Error: illegal operands `fnmadd\.q .*'
+.*: Error: illegal operands `fnmadd\.q .*'
+.*: Error: illegal operands `fmsub\.q .*'
+.*: Error: illegal operands `fmsub\.q .*'
+.*: Error: illegal operands `fmsub\.q .*'
+.*: Error: illegal operands `fmsub\.q .*'
+.*: Error: illegal operands `fnmsub\.q .*'
+.*: Error: illegal operands `fnmsub\.q .*'
+.*: Error: illegal operands `fnmsub\.q .*'
+.*: Error: illegal operands `fnmsub\.q .*'
+.*: Error: illegal operands `fcvt\.w\.q .*'
+.*: Error: illegal operands `fcvt\.wu\.q .*'
+.*: Error: illegal operands `fcvt\.q\.w .*'
+.*: Error: illegal operands `fcvt\.q\.wu .*'
+.*: Error: illegal operands `fcvt\.q\.s .*'
+.*: Error: illegal operands `fcvt\.s\.q .*'
+.*: Error: illegal operands `fcvt\.q\.d .*'
+.*: Error: illegal operands `fcvt\.d\.q .*'
+.*: Error: illegal operands `fcvt\.q\.h .*'
+.*: Error: illegal operands `fcvt\.h\.q .*'
+.*: Error: illegal operands `fsgnj\.q .*'
+.*: Error: illegal operands `fsgnjn\.q .*'
+.*: Error: illegal operands `fsgnjx\.q .*'
+.*: Error: illegal operands `feq\.q .*'
+.*: Error: illegal operands `flt\.q .*'
+.*: Error: illegal operands `fle\.q .*'
+.*: Error: illegal operands `fgt\.q .*'
+.*: Error: illegal operands `fge\.q .*'
+.*: Error: illegal operands `fmv\.q .*'
+.*: Error: illegal operands `fmv\.q .*'
+.*: Error: illegal operands `fneg\.q .*'
+.*: Error: illegal operands `fneg\.q .*'
+.*: Error: illegal operands `fabs\.q .*'
+.*: Error: illegal operands `fabs\.q .*'
+.*: Error: illegal operands `fclass\.q .*'
--- /dev/null
+++ b/gas/testsuite/gas/riscv/zqinx-rv64-fail.s
@@ -0,0 +1,70 @@ 
+target:
+	fadd.q		s1, s4, s8
+	fadd.q		s0, s5, s8
+	fadd.q		s0, s4, s9
+	fsub.q		s1, s4, s8
+	fsub.q		s0, s5, s8
+	fsub.q		s0, s4, s9
+	fmul.q		s1, s4, s8
+	fmul.q		s0, s5, s8
+	fmul.q		s0, s4, s9
+	fdiv.q		s1, s4, s8
+	fdiv.q		s0, s5, s8
+	fdiv.q		s0, s4, s9
+	fsqrt.q		s1, s4
+	fsqrt.q		s0, s5
+	fmin.q		s1, s4, s8
+	fmin.q		s0, s5, s8
+	fmin.q		s0, s4, s9
+	fmax.q		s1, s4, s8
+	fmax.q		s0, s5, s8
+	fmax.q		s0, s4, s9
+	fmadd.q		s1, s4, s8, a2
+	fmadd.q		s0, s5, s8, a2
+	fmadd.q		s0, s4, s9, a2
+	fmadd.q		s0, s4, s8, a3
+	fnmadd.q	s1, s4, s8, a2
+	fnmadd.q	s0, s5, s8, a2
+	fnmadd.q	s0, s4, s9, a2
+	fnmadd.q	s0, s4, s8, a3
+	fmsub.q		s1, s4, s8, a2
+	fmsub.q		s0, s5, s8, a2
+	fmsub.q		s0, s4, s9, a2
+	fmsub.q		s0, s4, s8, a3
+	fnmsub.q	s1, s4, s8, a2
+	fnmsub.q	s0, s5, s8, a2
+	fnmsub.q	s0, s4, s9, a2
+	fnmsub.q	s0, s4, s8, a3
+
+	fcvt.w.q	a0, s5
+	fcvt.wu.q	a0, s5
+	fcvt.q.w	s1, a0
+	fcvt.q.wu	s1, a0
+
+	fcvt.q.s	s1, a0
+	fcvt.s.q	a0, s5
+
+	fcvt.q.d	s1, a0
+	fcvt.d.q	a0, s5
+
+	.option push
+	.option arch, +zhinxmin
+	fcvt.q.h	s1, a0
+	fcvt.h.q	a0, s5
+	.option pop
+
+	fsgnj.q		s1, s4, s8
+	fsgnjn.q	s0, s5, s8
+	fsgnjx.q	s0, s4, s9
+	feq.q		a0, s5, s8
+	flt.q		a0, s4, s9
+	fle.q		a0, s5, s8
+	fgt.q		a0, s4, s9
+	fge.q		a0, s5, s8
+	fmv.q		s1, s4
+	fmv.q		s0, s5
+	fneg.q		s1, s4
+	fneg.q		s0, s5
+	fabs.q		s1, s4
+	fabs.q		s0, s5
+	fclass.q	a0, s5
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -691,6 +691,19 @@  struct riscv_opcode
 #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)
 
+/* Operands required to be an even-numbered register (pair) in RV64, and one
+   divisible by 4 on RV32.  */
+#define INSN_RV64_EVEN_D	0x00001000
+#define INSN_RV64_EVEN_S	0x00002000
+#define INSN_RV64_EVEN_T	0x00004000 /* Also covering U.  */
+#define INSN_RV64_EVEN_R	0x00008000
+/* Shorthands for combinations of the above.  */
+#define INSN_RV64_EVEN_DS	(INSN_RV64_EVEN_D   | INSN_RV64_EVEN_S)
+#define INSN_RV64_EVEN_DST	(INSN_RV64_EVEN_DS  | INSN_RV64_EVEN_T)
+#define INSN_RV64_EVEN_DSTR	(INSN_RV64_EVEN_DST | INSN_RV64_EVEN_R)
+#define INSN_RV64_EVEN_DU	(INSN_RV64_EVEN_D   | INSN_RV64_EVEN_T)
+#define INSN_RV64_EVEN_ST	(INSN_RV64_EVEN_S   | INSN_RV64_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
@@ -1049,6 +1049,25 @@  riscv_disassemble_insn (bfd_vma memaddr,
 		      && (word & (1u << OP_SH_RS3)))
 		    continue;
 		}
+
+	      if (pd->xlen <= 64
+		  && riscv_subset_supports (&pd->riscv_rps_dis, "zqinx"))
+		{
+		  unsigned int mask = pd->xlen == 32 ? 3 : 1;
+
+		  if ((op->pinfo & INSN_RV64_EVEN_D)
+		      && (word & (mask << OP_SH_RD)))
+		    continue;
+		  if ((op->pinfo & INSN_RV64_EVEN_S)
+		      && (word & (mask << OP_SH_RS1)))
+		    continue;
+		  if ((op->pinfo & INSN_RV64_EVEN_T)
+		      && (word & (mask << OP_SH_RS2)))
+		    continue;
+		  if ((op->pinfo & INSN_RV64_EVEN_R)
+		      && (word & (mask << OP_SH_RS3)))
+		    continue;
+		}
 	    }
 
 	  /* It's a match.  */
--- a/opcodes/riscv-opc.c
+++ b/opcodes/riscv-opc.c
@@ -932,10 +932,10 @@  const struct riscv_opcode riscv_opcodes[
 {"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, 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.q.h",   0, INSN_CLASS_ZFHMIN_AND_Q_INX, "D,S", MATCH_FCVT_Q_H, MASK_FCVT_Q_H|MASK_RM, match_opcode, INSN_RV64_EVEN_D },
 {"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, 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 },
+{"fcvt.h.q",   0, INSN_CLASS_ZFHMIN_AND_Q_INX, "D,Sm", MATCH_FCVT_H_Q, MASK_FCVT_H_Q, match_opcode, INSN_RV64_EVEN_S },
 {"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 },
 {"flt.h",      0, INSN_CLASS_ZFH_INX,   "d,S,T",     MATCH_FLT_H, MASK_FLT_H, match_opcode, 0 },
@@ -1062,41 +1062,41 @@  const struct riscv_opcode riscv_opcodes[
 {"flq",        0, INSN_CLASS_Q,   "D,A,s",     0, (int) M_FLx, match_rs1_nonzero, INSN_MACRO },
 {"fsq",        0, INSN_CLASS_Q,   "T,q(s)",    MATCH_FSQ, MASK_FSQ, match_opcode, INSN_DREF|INSN_16_BYTE },
 {"fsq",        0, INSN_CLASS_Q,   "T,A,s",     0, (int) M_Sx_FSx, match_rs1_nonzero, INSN_MACRO },
-{"fmv.q",      0, INSN_CLASS_Q_INX,   "D,U",       MATCH_FSGNJ_Q, MASK_FSGNJ_Q, match_rs1_eq_rs2, INSN_ALIAS },
-{"fneg.q",     0, INSN_CLASS_Q_INX,   "D,U",       MATCH_FSGNJN_Q, MASK_FSGNJN_Q, match_rs1_eq_rs2, INSN_ALIAS },
-{"fabs.q",     0, INSN_CLASS_Q_INX,   "D,U",       MATCH_FSGNJX_Q, MASK_FSGNJX_Q, match_rs1_eq_rs2, INSN_ALIAS },
-{"fsgnj.q",    0, INSN_CLASS_Q_INX,   "D,S,T",     MATCH_FSGNJ_Q, MASK_FSGNJ_Q, match_opcode, 0 },
-{"fsgnjn.q",   0, INSN_CLASS_Q_INX,   "D,S,T",     MATCH_FSGNJN_Q, MASK_FSGNJN_Q, match_opcode, 0 },
-{"fsgnjx.q",   0, INSN_CLASS_Q_INX,   "D,S,T",     MATCH_FSGNJX_Q, MASK_FSGNJX_Q, match_opcode, 0 },
-{"fadd.q",     0, INSN_CLASS_Q_INX,   "D,S,Tm",    MATCH_FADD_Q, MASK_FADD_Q, match_opcode, 0 },
-{"fsub.q",     0, INSN_CLASS_Q_INX,   "D,S,Tm",    MATCH_FSUB_Q, MASK_FSUB_Q, match_opcode, 0 },
-{"fmul.q",     0, INSN_CLASS_Q_INX,   "D,S,Tm",    MATCH_FMUL_Q, MASK_FMUL_Q, match_opcode, 0 },
-{"fdiv.q",     0, INSN_CLASS_Q_INX,   "D,S,Tm",    MATCH_FDIV_Q, MASK_FDIV_Q, match_opcode, 0 },
-{"fsqrt.q",    0, INSN_CLASS_Q_INX,   "D,Sm",      MATCH_FSQRT_Q, MASK_FSQRT_Q, match_opcode, 0 },
-{"fmin.q",     0, INSN_CLASS_Q_INX,   "D,S,T",     MATCH_FMIN_Q, MASK_FMIN_Q, match_opcode, 0 },
-{"fmax.q",     0, INSN_CLASS_Q_INX,   "D,S,T",     MATCH_FMAX_Q, MASK_FMAX_Q, match_opcode, 0 },
-{"fmadd.q",    0, INSN_CLASS_Q_INX,   "D,S,T,Rm",  MATCH_FMADD_Q, MASK_FMADD_Q, match_opcode, 0 },
-{"fnmadd.q",   0, INSN_CLASS_Q_INX,   "D,S,T,Rm",  MATCH_FNMADD_Q, MASK_FNMADD_Q, match_opcode, 0 },
-{"fmsub.q",    0, INSN_CLASS_Q_INX,   "D,S,T,Rm",  MATCH_FMSUB_Q, MASK_FMSUB_Q, match_opcode, 0 },
-{"fnmsub.q",   0, INSN_CLASS_Q_INX,   "D,S,T,Rm",  MATCH_FNMSUB_Q, MASK_FNMSUB_Q, match_opcode, 0 },
-{"fcvt.w.q",   0, INSN_CLASS_Q_INX,   "d,Sm",      MATCH_FCVT_W_Q, MASK_FCVT_W_Q, match_opcode, 0 },
-{"fcvt.wu.q",  0, INSN_CLASS_Q_INX,   "d,Sm",      MATCH_FCVT_WU_Q, MASK_FCVT_WU_Q, match_opcode, 0 },
-{"fcvt.q.w",   0, INSN_CLASS_Q_INX,   "D,s",       MATCH_FCVT_Q_W, MASK_FCVT_Q_W|MASK_RM, match_opcode, 0 },
-{"fcvt.q.wu",  0, INSN_CLASS_Q_INX,   "D,s",       MATCH_FCVT_Q_WU, MASK_FCVT_Q_WU|MASK_RM, match_opcode, 0 },
-{"fcvt.q.s",   0, INSN_CLASS_Q_INX,   "D,S",       MATCH_FCVT_Q_S, MASK_FCVT_Q_S|MASK_RM, match_opcode, 0 },
-{"fcvt.q.d",   0, INSN_CLASS_Q_INX,   "D,S",       MATCH_FCVT_Q_D, MASK_FCVT_Q_D|MASK_RM, match_opcode, 0 },
-{"fcvt.s.q",   0, INSN_CLASS_Q_INX,   "D,Sm",      MATCH_FCVT_S_Q, MASK_FCVT_S_Q, match_opcode, 0 },
-{"fcvt.d.q",   0, INSN_CLASS_Q_INX,   "D,Sm",      MATCH_FCVT_D_Q, MASK_FCVT_D_Q, match_opcode, 0 },
-{"fclass.q",   0, INSN_CLASS_Q_INX,   "d,S",       MATCH_FCLASS_Q, MASK_FCLASS_Q, match_opcode, 0 },
-{"feq.q",      0, INSN_CLASS_Q_INX,   "d,S,T",     MATCH_FEQ_Q, MASK_FEQ_Q, match_opcode, 0 },
-{"flt.q",      0, INSN_CLASS_Q_INX,   "d,S,T",     MATCH_FLT_Q, MASK_FLT_Q, match_opcode, 0 },
-{"fle.q",      0, INSN_CLASS_Q_INX,   "d,S,T",     MATCH_FLE_Q, MASK_FLE_Q, match_opcode, 0 },
-{"fgt.q",      0, INSN_CLASS_Q_INX,   "d,T,S",     MATCH_FLT_Q, MASK_FLT_Q, match_opcode, INSN_ALIAS },
-{"fge.q",      0, INSN_CLASS_Q_INX,   "d,T,S",     MATCH_FLE_Q, MASK_FLE_Q, match_opcode, INSN_ALIAS },
-{"fcvt.l.q",  64, INSN_CLASS_Q_INX,   "d,Sm",      MATCH_FCVT_L_Q, MASK_FCVT_L_Q, match_opcode, 0 },
-{"fcvt.lu.q", 64, INSN_CLASS_Q_INX,   "d,Sm",      MATCH_FCVT_LU_Q, MASK_FCVT_LU_Q, match_opcode, 0 },
-{"fcvt.q.l",  64, INSN_CLASS_Q_INX,   "D,s",       MATCH_FCVT_Q_L, MASK_FCVT_Q_L|MASK_RM, match_opcode, 0 },
-{"fcvt.q.lu", 64, INSN_CLASS_Q_INX,   "D,s",       MATCH_FCVT_Q_LU, MASK_FCVT_Q_LU|MASK_RM, match_opcode, 0 },
+{"fmv.q",      0, INSN_CLASS_Q_INX,   "D,U",       MATCH_FSGNJ_Q, MASK_FSGNJ_Q, match_rs1_eq_rs2, INSN_ALIAS|INSN_RV64_EVEN_DU },
+{"fneg.q",     0, INSN_CLASS_Q_INX,   "D,U",       MATCH_FSGNJN_Q, MASK_FSGNJN_Q, match_rs1_eq_rs2, INSN_ALIAS|INSN_RV64_EVEN_DU },
+{"fabs.q",     0, INSN_CLASS_Q_INX,   "D,U",       MATCH_FSGNJX_Q, MASK_FSGNJX_Q, match_rs1_eq_rs2, INSN_ALIAS|INSN_RV64_EVEN_DU },
+{"fsgnj.q",    0, INSN_CLASS_Q_INX,   "D,S,T",     MATCH_FSGNJ_Q, MASK_FSGNJ_Q, match_opcode, INSN_RV64_EVEN_DST },
+{"fsgnjn.q",   0, INSN_CLASS_Q_INX,   "D,S,T",     MATCH_FSGNJN_Q, MASK_FSGNJN_Q, match_opcode, INSN_RV64_EVEN_DST },
+{"fsgnjx.q",   0, INSN_CLASS_Q_INX,   "D,S,T",     MATCH_FSGNJX_Q, MASK_FSGNJX_Q, match_opcode, INSN_RV64_EVEN_DST },
+{"fadd.q",     0, INSN_CLASS_Q_INX,   "D,S,Tm",    MATCH_FADD_Q, MASK_FADD_Q, match_opcode, INSN_RV64_EVEN_DST },
+{"fsub.q",     0, INSN_CLASS_Q_INX,   "D,S,Tm",    MATCH_FSUB_Q, MASK_FSUB_Q, match_opcode, INSN_RV64_EVEN_DST },
+{"fmul.q",     0, INSN_CLASS_Q_INX,   "D,S,Tm",    MATCH_FMUL_Q, MASK_FMUL_Q, match_opcode, INSN_RV64_EVEN_DST },
+{"fdiv.q",     0, INSN_CLASS_Q_INX,   "D,S,Tm",    MATCH_FDIV_Q, MASK_FDIV_Q, match_opcode, INSN_RV64_EVEN_DST },
+{"fsqrt.q",    0, INSN_CLASS_Q_INX,   "D,Sm",      MATCH_FSQRT_Q, MASK_FSQRT_Q, match_opcode, INSN_RV64_EVEN_DS },
+{"fmin.q",     0, INSN_CLASS_Q_INX,   "D,S,T",     MATCH_FMIN_Q, MASK_FMIN_Q, match_opcode, INSN_RV64_EVEN_DST },
+{"fmax.q",     0, INSN_CLASS_Q_INX,   "D,S,T",     MATCH_FMAX_Q, MASK_FMAX_Q, match_opcode, INSN_RV64_EVEN_DST },
+{"fmadd.q",    0, INSN_CLASS_Q_INX,   "D,S,T,Rm",  MATCH_FMADD_Q, MASK_FMADD_Q, match_opcode, INSN_RV64_EVEN_DSTR },
+{"fnmadd.q",   0, INSN_CLASS_Q_INX,   "D,S,T,Rm",  MATCH_FNMADD_Q, MASK_FNMADD_Q, match_opcode, INSN_RV64_EVEN_DSTR },
+{"fmsub.q",    0, INSN_CLASS_Q_INX,   "D,S,T,Rm",  MATCH_FMSUB_Q, MASK_FMSUB_Q, match_opcode, INSN_RV64_EVEN_DSTR },
+{"fnmsub.q",   0, INSN_CLASS_Q_INX,   "D,S,T,Rm",  MATCH_FNMSUB_Q, MASK_FNMSUB_Q, match_opcode, INSN_RV64_EVEN_DSTR },
+{"fcvt.w.q",   0, INSN_CLASS_Q_INX,   "d,Sm",      MATCH_FCVT_W_Q, MASK_FCVT_W_Q, match_opcode, INSN_RV64_EVEN_S },
+{"fcvt.wu.q",  0, INSN_CLASS_Q_INX,   "d,Sm",      MATCH_FCVT_WU_Q, MASK_FCVT_WU_Q, match_opcode, INSN_RV64_EVEN_S },
+{"fcvt.q.w",   0, INSN_CLASS_Q_INX,   "D,s",       MATCH_FCVT_Q_W, MASK_FCVT_Q_W|MASK_RM, match_opcode, INSN_RV64_EVEN_D },
+{"fcvt.q.wu",  0, INSN_CLASS_Q_INX,   "D,s",       MATCH_FCVT_Q_WU, MASK_FCVT_Q_WU|MASK_RM, match_opcode, INSN_RV64_EVEN_D },
+{"fcvt.q.s",   0, INSN_CLASS_Q_INX,   "D,S",       MATCH_FCVT_Q_S, MASK_FCVT_Q_S|MASK_RM, match_opcode, INSN_RV64_EVEN_D },
+{"fcvt.q.d",   0, INSN_CLASS_Q_INX,   "D,S",       MATCH_FCVT_Q_D, MASK_FCVT_Q_D|MASK_RM, match_opcode, INSN_RV64_EVEN_D|INSN_RV32_EVEN_S },
+{"fcvt.s.q",   0, INSN_CLASS_Q_INX,   "D,Sm",      MATCH_FCVT_S_Q, MASK_FCVT_S_Q, match_opcode, INSN_RV64_EVEN_S },
+{"fcvt.d.q",   0, INSN_CLASS_Q_INX,   "D,Sm",      MATCH_FCVT_D_Q, MASK_FCVT_D_Q, match_opcode, INSN_RV32_EVEN_D|INSN_RV64_EVEN_S },
+{"fclass.q",   0, INSN_CLASS_Q_INX,   "d,S",       MATCH_FCLASS_Q, MASK_FCLASS_Q, match_opcode, INSN_RV64_EVEN_S },
+{"feq.q",      0, INSN_CLASS_Q_INX,   "d,S,T",     MATCH_FEQ_Q, MASK_FEQ_Q, match_opcode, INSN_RV64_EVEN_ST },
+{"flt.q",      0, INSN_CLASS_Q_INX,   "d,S,T",     MATCH_FLT_Q, MASK_FLT_Q, match_opcode, INSN_RV64_EVEN_ST },
+{"fle.q",      0, INSN_CLASS_Q_INX,   "d,S,T",     MATCH_FLE_Q, MASK_FLE_Q, match_opcode, INSN_RV64_EVEN_ST },
+{"fgt.q",      0, INSN_CLASS_Q_INX,   "d,T,S",     MATCH_FLT_Q, MASK_FLT_Q, match_opcode, INSN_ALIAS|INSN_RV64_EVEN_ST },
+{"fge.q",      0, INSN_CLASS_Q_INX,   "d,T,S",     MATCH_FLE_Q, MASK_FLE_Q, match_opcode, INSN_ALIAS|INSN_RV64_EVEN_ST },
+{"fcvt.l.q",  64, INSN_CLASS_Q_INX,   "d,Sm",      MATCH_FCVT_L_Q, MASK_FCVT_L_Q, match_opcode, INSN_RV64_EVEN_S },
+{"fcvt.lu.q", 64, INSN_CLASS_Q_INX,   "d,Sm",      MATCH_FCVT_LU_Q, MASK_FCVT_LU_Q, match_opcode, INSN_RV64_EVEN_S },
+{"fcvt.q.l",  64, INSN_CLASS_Q_INX,   "D,s",       MATCH_FCVT_Q_L, MASK_FCVT_Q_L|MASK_RM, match_opcode, INSN_RV64_EVEN_D },
+{"fcvt.q.lu", 64, INSN_CLASS_Q_INX,   "D,s",       MATCH_FCVT_Q_LU, MASK_FCVT_Q_LU|MASK_RM, match_opcode, INSN_RV64_EVEN_D },
 
 /* Compressed instructions.  */
 {"c.unimp",    0, INSN_CLASS_ZCA,   "",          0, 0xffffU,  match_opcode, 0 },