[v4,06/12] RISC-V: check operands for Zdinx in RV32

Message ID 8873df38-aa72-4643-91b3-4037643b2717@suse.com
State New
Headers
Series RISC-V: assorted fixes and (hopefully) improvements |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 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 July 24, 2026, 10:04 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.
---
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.)
  

Comments

Nelson Chu July 27, 2026, 3:08 a.m. UTC | #1
I used to have reservations about these assembler constraint checks
(including those for RVV) because, at the time, there were strong
objections from people whose random test case generators were failing
due to this feature. Essentially, the project involves randomly
generating various assembler instructions (including illegal ones) and
feeding them to the hardware to verify whether the DV (Design
Verification) setup correctly detects the invalid instructions.  There
was previously a compromise involving an option to let users toggle
these checks on or off, but I didn't think the option was a good idea
- since it was specific to RVV and addressed only a very small number
of edge cases - so I ultimately removed it.  In fact, people can still
assemble these illegal instructions by .insn directives, but it just
wasn't adopted at the time due to the complex certain political
reasons.  So now I think it's time to correct the wrong decision and
do the right thing - I think what you are doing is right, allowing
illegal instructions for assembler is wrong and dangerous for general
users, so this patch (and maybe further patches for constraints) looks
good to me.

Thanks
Nelson

On Fri, Jul 24, 2026 at 6:04 PM Jan Beulich <jbeulich@suse.com> wrote:
>
> 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.
> ---
> 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.)
>
> --- a/gas/config/tc-riscv.c
> +++ b/gas/config/tc-riscv.c
> @@ -3004,6 +3004,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;
> @@ -3011,6 +3017,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;
> @@ -3670,32 +3678,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;
>
> @@ -4530,6 +4546,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
> @@ -709,6 +709,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
> @@ -1138,6 +1138,23 @@ riscv_disassemble_insn (bfd_vma memaddr,
>               if (!riscv_multi_subset_supports (&pd->riscv_rps_dis,
>                                                 op->insn_class))
>                 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
> @@ -955,10 +955,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 },
> @@ -1045,35 +1045,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 },
>
  

Patch

--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -3004,6 +3004,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;
@@ -3011,6 +3017,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;
@@ -3670,32 +3678,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;
 
@@ -4530,6 +4546,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
@@ -709,6 +709,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
@@ -1138,6 +1138,23 @@  riscv_disassemble_insn (bfd_vma memaddr,
 	      if (!riscv_multi_subset_supports (&pd->riscv_rps_dis,
 						op->insn_class))
 		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
@@ -955,10 +955,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 },
@@ -1045,35 +1045,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 },