[PATCH] RISC-V: Add support for XW extension on some WCH cores

Andy Lin andylinpersonal@gmail.com
Mon Nov 24 09:18:27 GMT 2025


The original toolchain use `-xw'. I can change the `-march' string to
`-xwchc' but it
will break the compatibility with the vendor toolchain. Btw, I'm not a
WCH guy. Should
I just create a PR on `riscv-toolchain-conventions' for them?

Kito Cheng <kito.cheng@gmail.com> 於 2025年11月24日 週一 下午2:09寫道:
>
> Also could you create a PR to
> https://github.com/riscv-non-isa/riscv-toolchain-conventions/blob/main/src/toolchain-conventions.adoc#list-of-vendor-prefixes
> ?
>
> On Mon, Nov 24, 2025 at 10:39 AM Kito Cheng <kito.cheng@gmail.com> wrote:
> >
> > Why it's xwchc in LLVM but xw here?
> >
> > On Mon, Nov 24, 2025 at 3:50 AM Andy Lin <andylinpersonal@gmail.com> wrote:
> > >
> > >   This patch implements the xw extension found on some QingKe cores from
> > > QinHeng (WCH). BTW, there is one divergence between this patch and vendor's
> > > toolchain (based on older binutils 2.38.0) when using `norvc', `rvc' and
> > > `arch' directives:
> > >
> > > Vendor's toolchain:
> > >
> > >   `-march' with `_xw':
> > >     After `.option rvc', xw opcodes are available implicitly.
> > >
> > >   `-march' without `_xw':
> > >     After `.option rvc', xw opcodes are NOT available, even if we explicitly
> > >     add the `.option arch,+xw'.
> > >
> > > This patch:
> > >
> > >   `-march' with or without `_xw':
> > >     After `.option rvc', xw opcodes are not available implicitly, but user
> > >     can add `.option arch,+xw' to enable it. This behavior is consistent
> > >     with other Zc* extensions not covered by the existing C extension.
> > >
> > > [1] Specs: http://web.archive.org/web/20251121133229/https://discourse.llvm.org/t/rfc-supporting-wch-qingke-xw-compressed-opcodes/79392/12
> > > [2] LLVM's merged patch: https://github.com/llvm/llvm-project/pull/97925/files
> > >
> > > bfd/ChangeLog:
> > >
> > >         * elfxx-riscv.c (riscv_multi_subset_supports): Add support for
> > >         XW extension.
> > >         * (riscv_multi_subset_supports_ext): Likewise.
> > >         * (riscv_update_subset_norvc): XW should not be emitted
> > >         under `norvc' mode.
> > >
> > > gas/ChangeLog:
> > >
> > >         * config/tc-riscv.c (riscv_ip): Add XW to assembler.
> > >         * config/tc-riscv.c (validate_riscv_insn): Add XW.
> > >         * doc/c-riscv.texi: Note XW as an additional ISA extension for some
> > >         WCH's QingKe RISC-V MCU cores.
> > >         * testsuite/gas/riscv/march-help.l: Add '-xw' string.
> > >         * testsuite/gas/riscv/x-wch-qk-xw.d: New tests.
> > >         * testsuite/gas/riscv/x-wch-qk-xw.s: Likewise.
> > >         * testsuite/gas/riscv/x-wch-qk-xw-fail.d: Likewise.
> > >         * testsuite/gas/riscv/x-wch-qk-xw-fail.l: Likewise.
> > >         * testsuite/gas/riscv/x-wch-qk-xw-fail.s: Likewise.
> > >         * testsuite/gas/riscv/x-wch-qk-xw-norvc.d: New test for `norvc'.
> > >         * testsuite/gas/riscv/x-wch-qk-xw-norvc.s: Likewise.
> > >
> > > include/ChangeLog:
> > >
> > >         * opcode/riscv-opc.h: Add MATCH and MASK opcode macros for XW.
> > >         * opcode/riscv.h (riscv_insn_class): Add INSN_CLASS_XWCH_QK.
> > >
> > > opcodes/ChangeLog:
> > >
> > >         * riscv-opc.c (riscv_opcodes): Add XW opcodes and aliases.
> > >         * riscv-dis.c (print_insn_args): Add XW opcode to disassembler.
> > >
> > > Signed-off-by: Andy Lin <andylinpersonal@gmail.com>
> > > ---
> > >  bfd/ChangeLog                               |  8 ++
> > >  bfd/elfxx-riscv.c                           | 13 +++-
> > >  gas/ChangeLog                               | 15 ++++
> > >  gas/NEWS                                    |  7 +-
> > >  gas/config/tc-riscv.c                       | 62 ++++++++++++++-
> > >  gas/doc/c-riscv.texi                        |  6 ++
> > >  gas/testsuite/gas/riscv/march-help.l        |  1 +
> > >  gas/testsuite/gas/riscv/x-wch-qk-xw-fail.d  |  3 +
> > >  gas/testsuite/gas/riscv/x-wch-qk-xw-fail.l  | 33 ++++++++
> > >  gas/testsuite/gas/riscv/x-wch-qk-xw-fail.s  | 39 ++++++++++
> > >  gas/testsuite/gas/riscv/x-wch-qk-xw-norvc.d | 26 +++++++
> > >  gas/testsuite/gas/riscv/x-wch-qk-xw-norvc.s | 20 +++++
> > >  gas/testsuite/gas/riscv/x-wch-qk-xw.d       | 81 +++++++++++++++++++
> > >  gas/testsuite/gas/riscv/x-wch-qk-xw.s       | 86 +++++++++++++++++++++
> > >  include/ChangeLog                           |  5 ++
> > >  include/opcode/riscv-opc.h                  | 26 +++++++
> > >  include/opcode/riscv.h                      | 24 ++++++
> > >  opcodes/ChangeLog                           |  5 ++
> > >  opcodes/riscv-dis.c                         | 23 ++++++
> > >  opcodes/riscv-opc.c                         | 18 +++++
> > >  20 files changed, 497 insertions(+), 4 deletions(-)
> > >  create mode 100644 gas/testsuite/gas/riscv/x-wch-qk-xw-fail.d
> > >  create mode 100644 gas/testsuite/gas/riscv/x-wch-qk-xw-fail.l
> > >  create mode 100644 gas/testsuite/gas/riscv/x-wch-qk-xw-fail.s
> > >  create mode 100644 gas/testsuite/gas/riscv/x-wch-qk-xw-norvc.d
> > >  create mode 100644 gas/testsuite/gas/riscv/x-wch-qk-xw-norvc.s
> > >  create mode 100644 gas/testsuite/gas/riscv/x-wch-qk-xw.d
> > >  create mode 100644 gas/testsuite/gas/riscv/x-wch-qk-xw.s
> > >
> > > diff --git a/bfd/ChangeLog b/bfd/ChangeLog
> > > index 1bd99a9b042..c41ae349f71 100644
> > > --- a/bfd/ChangeLog
> > > +++ b/bfd/ChangeLog
> > > @@ -1,3 +1,11 @@
> > > +2025-11-23  Andy Lin <andylinpersonal@gmail.com>
> > > +
> > > +       * elfxx-riscv.c (riscv_multi_subset_supports): Add support for
> > > +       XW extension.
> > > +       * (riscv_multi_subset_supports_ext): Likewise.
> > > +       * (riscv_update_subset_norvc): XW should not be emitted
> > > +       under `norvc' mode.
> > > +
> > >  2025-09-16  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>
> > >
> > >         * config.bfd <powerpc-*-solaris2*>: Remove.
> > > diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
> > > index 3b92fbed5f0..5c2948b5b93 100644
> > > --- a/bfd/elfxx-riscv.c
> > > +++ b/bfd/elfxx-riscv.c
> > > @@ -1641,6 +1641,7 @@ static const struct riscv_supported_ext riscv_supported_vendor_x_ext[] =
> > >    {"xmipscmov",                ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
> > >    {"xmipsexectl",      ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
> > >    {"xmipslsp",         ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
> > > +  {"xw",               ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
> > >    {NULL, 0, 0, 0, 0}
> > >  };
> > >
> > > @@ -2291,6 +2292,12 @@ riscv_parse_check_conflicts (riscv_parse_subset_t *rps)
> > >                           xlen);
> > >        no_conflict = false;
> > >      }
> > > +  if (riscv_subset_supports (rps, "xw")
> > > +      && (riscv_subset_supports (rps, "d") || riscv_subset_supports (rps, "zcb")))
> > > +    {
> > > +      rps->error_handler (_("xw' is conflict with the `d/zcb' extension"));
> > > +      no_conflict = false;
> > > +    }
> > >
> > >    bool support_zve = false;
> > >    bool support_zvl = false;
> > > @@ -2776,7 +2783,7 @@ riscv_update_subset_norvc (riscv_parse_subset_t *rps)
> > >  {
> > >    return riscv_update_subset1 (rps, rps->subset_list->head,
> > >                                "-c,-zca,-zcd,-zcf,-zcb,-zce,-zcmp,-zcmt,"
> > > -                              "-zcmop,-zclsd");
> > > +                              "-zcmop,-zclsd,-xw");
> > >  }
> > >
> > >  /* Check if the FEATURE subset is supported or not in the subset list.
> > > @@ -3059,6 +3066,8 @@ riscv_multi_subset_supports (riscv_parse_subset_t *rps,
> > >        return riscv_subset_supports (rps, "xmipsexectl");
> > >      case INSN_CLASS_XMIPSLSP:
> > >        return riscv_subset_supports (rps, "xmipslsp");
> > > +    case INSN_CLASS_XWCH_QK:
> > > +      return riscv_subset_supports (rps, "xw");
> > >      default:
> > >        rps->error_handler
> > >          (_("internal: unreachable INSN_CLASS_*"));
> > > @@ -3362,6 +3371,8 @@ riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
> > >        return "xsfvqmaccdod";
> > >      case INSN_CLASS_XSFVFNRCLIPXFQF:
> > >        return "xsfvfnrclipxfqf";
> > > +    case INSN_CLASS_XWCH_QK:
> > > +      return "xw";
> > >      default:
> > >        rps->error_handler
> > >          (_("internal: unreachable INSN_CLASS_*"));
> > > diff --git a/gas/ChangeLog b/gas/ChangeLog
> > > index dd7691593f1..ff10bca004b 100644
> > > --- a/gas/ChangeLog
> > > +++ b/gas/ChangeLog
> > > @@ -1,3 +1,18 @@
> > > +2025-11-23  Andy Lin <andylinpersonal@gmail.com>
> > > +
> > > +       * config/tc-riscv.c (riscv_ip): Add XW to assembler.
> > > +       * config/tc-riscv.c (validate_riscv_insn): Add XW.
> > > +       * doc/c-riscv.texi: Note XW as an additional ISA extension for some
> > > +       WCH's QingKe RISC-V MCU cores.
> > > +       * testsuite/gas/riscv/march-help.l: Add '-xw' string.
> > > +       * testsuite/gas/riscv/x-wch-qk-xw.d: New tests.
> > > +       * testsuite/gas/riscv/x-wch-qk-xw.s: Likewise.
> > > +       * testsuite/gas/riscv/x-wch-qk-xw-fail.d: Likewise.
> > > +       * testsuite/gas/riscv/x-wch-qk-xw-fail.l: Likewise.
> > > +       * testsuite/gas/riscv/x-wch-qk-xw-fail.s: Likewise.
> > > +       * testsuite/gas/riscv/x-wch-qk-xw-norvc.d: New test for `norvc'.
> > > +       * testsuite/gas/riscv/x-wch-qk-xw-norvc.s: Likewise.
> > > +
> > >  2025-09-16  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>
> > >
> > >         * NEWS: Mention Solaris/PowerPC removal.
> > > diff --git a/gas/NEWS b/gas/NEWS
> > > index 4bd3d747aa5..ffeeb93aad8 100644
> > > --- a/gas/NEWS
> > > +++ b/gas/NEWS
> > > @@ -1,5 +1,8 @@
> > >  -*- text -*-
> > >
> > > +* Add support for RISC-V vendor extension:
> > > +  QinHeng (WCH): xw v1.0.
> > > +
> > >  * Emit an SFrame FRE with zero offsets to convey an undefined return address
> > >    in the SFrame stack trace format.
> > >
> > > @@ -133,7 +136,7 @@ Changes in 2.43:
> > >
> > >  * Add a .base64 directive to the assembler which allows base64 encoded
> > >    binary data to be provided as strings.
> > > -
> > > +
> > >  * Add support for 'armv9.5-a' for -march in AArch64 GAS.
> > >
> > >  * In x86 Intel syntax undue mnemonic suffixes are now warned about.  This is
> > > @@ -143,7 +146,7 @@ Changes in 2.43:
> > >    the syntax \+ to access the number of times a given macro has been executed.
> > >    This is similar to the already existing \@ syntax, except that the count is
> > >    maintained on a per-macro basis.
> > > -
> > > +
> > >  * Support the NF feature in Intel APX.
> > >
> > >  * Remove KEYLOCKER and SHA promotions from EVEX MAP4.
> > > diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
> > > index df60c206b82..28bfc87a7f8 100644
> > > --- a/gas/config/tc-riscv.c
> > > +++ b/gas/config/tc-riscv.c
> > > @@ -1771,6 +1771,17 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
> > >                     goto unknown_validate_operand;
> > >                 }
> > >                 break;
> > > +           case 'w': /* Vendor-specific (Qinheng QingKe Xw) operands. */
> > > +             switch(*++oparg)
> > > +               {
> > > +                 case 'b': used_bits |= ENCODE_WCH_QK_UIMM5 (-1); break; /* Xwb */
> > > +                 case '6': used_bits |= ENCODE_WCH_QK_UIMM6_LSB0 (-1); break; /* Xw6 */
> > > +                 case '4': used_bits |= ENCODE_WCH_QK_UIMM4 (-1); break; /* Xw4 */
> > > +                 case '5': used_bits |= ENCODE_WCH_QK_UIMM5_LSB0 (-1); break; /* Xw5 */
> > > +                 default:
> > > +                   goto unknown_validate_operand;
> > > +               }
> > > +               break;
> > >             default:
> > >               goto unknown_validate_operand;
> > >             }
> > > @@ -4287,6 +4298,54 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
> > >                     }
> > >                   break;
> > >
> > > +               case 'w': /* Vendor-specific (Qinheng QingKe Xw) operands. */
> > > +                 switch (*++oparg)
> > > +                   {
> > > +                   case 'b': /* Xwb: uimm5 0-31. */
> > > +                     /* For qk.c.lbu and qk.c.sb. */
> > > +                     if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
> > > +                       continue;
> > > +                     if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
> > > +                         || imm_expr->X_op != O_constant
> > > +                         || !VALID_WCH_QK_UIMM5 ((valueT) imm_expr->X_add_number))
> > > +                         break;
> > > +                     ip->insn_opcode |= ENCODE_WCH_QK_UIMM5(imm_expr->X_add_number);
> > > +                     goto rvc_imm_done;
> > > +                   case '6': /* Xw6: uimm6_lsb0 0-62. Must be multiple of 2. */
> > > +                     /* For qk.c.lhu and qk.c.sh. */
> > > +                     if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
> > > +                       continue;
> > > +                     if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
> > > +                         || imm_expr->X_op != O_constant
> > > +                         || !VALID_WCH_QK_UIMM6_LSB0 ((valueT) imm_expr->X_add_number))
> > > +                         break;
> > > +                     ip->insn_opcode |= ENCODE_WCH_QK_UIMM6_LSB0(imm_expr->X_add_number);
> > > +                     goto rvc_imm_done;
> > > +                   case '4': /* Xw4: uimm4 0-15. */
> > > +                     /* For qk.c.lbusp and qk.c.sbsp. */
> > > +                     if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
> > > +                       continue;
> > > +                     if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
> > > +                         || imm_expr->X_op != O_constant
> > > +                         || !VALID_WCH_QK_UIMM4 ((valueT) imm_expr->X_add_number))
> > > +                         break;
> > > +                     ip->insn_opcode |= ENCODE_WCH_QK_UIMM4(imm_expr->X_add_number);
> > > +                     goto rvc_imm_done;
> > > +                   case '5': /* Xw5: uimm5_lsb0 0-30. Must be multiple of 2. */
> > > +                     /* For qk.c.lhusp and qk.c.shsp. */
> > > +                     if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
> > > +                       continue;
> > > +                     if (my_getSmallExpression (imm_expr, imm_reloc, asarg, p)
> > > +                         || imm_expr->X_op != O_constant
> > > +                         || !VALID_WCH_QK_UIMM5_LSB0 ((valueT) imm_expr->X_add_number))
> > > +                         break;
> > > +                     ip->insn_opcode |= ENCODE_WCH_QK_UIMM5_LSB0(imm_expr->X_add_number);
> > > +                     goto rvc_imm_done;
> > > +                   default:
> > > +                     goto unknown_riscv_ip_operand;
> > > +                   }
> > > +                 break;
> > > +
> > >                 default:
> > >                   goto unknown_riscv_ip_operand;
> > >                 }
> > > @@ -5109,7 +5168,8 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
> > >        riscv_arch_str (xlen, riscv_rps_as.subset_list, true/* update */);
> > >
> > >        riscv_set_rvc (riscv_subset_supports (&riscv_rps_as, "c")
> > > -                    || riscv_subset_supports (&riscv_rps_as, "zca"));
> > > +                    || riscv_subset_supports (&riscv_rps_as, "zca")
> > > +                    || riscv_subset_supports (&riscv_rps_as, "xw"));
> > >
> > >        if (riscv_subset_supports (&riscv_rps_as, "ztso"))
> > >         riscv_set_tso ();
> > > diff --git a/gas/doc/c-riscv.texi b/gas/doc/c-riscv.texi
> > > index ea4be32a8ff..d843bfd9d50 100644
> > > --- a/gas/doc/c-riscv.texi
> > > +++ b/gas/doc/c-riscv.texi
> > > @@ -918,4 +918,10 @@ The XMipsSlsp extension provides instructions mips.ldp, mips.lwp, mips.sdp and m
> > >
> > >  It is documented in @url{https://mips.com/wp-content/uploads/2025/03/P8700-F_Programmers_Reference_Manual_Rev1.82_3-19-2025.pdf}.
> > >
> > > +@item XW
> > > +
> > > +The XW extension provide additional compressed opcodes present in some QingKe cores from Nanjing Qinheng Microelectronics Co., Ltd. (WCH).
> > > +
> > > +It is documented in @url{http://web.archive.org/web/20251121133229/https://discourse.llvm.org/t/rfc-supporting-wch-qingke-xw-compressed-opcodes/79392/12}
> > > +
> > >  @end table
> > > diff --git a/gas/testsuite/gas/riscv/march-help.l b/gas/testsuite/gas/riscv/march-help.l
> > > index 0ce2f896735..aa1632ef606 100644
> > > --- a/gas/testsuite/gas/riscv/march-help.l
> > > +++ b/gas/testsuite/gas/riscv/march-help.l
> > > @@ -186,3 +186,4 @@ All available -march extensions for RISC-V:
> > >         xmipscmov                               1.0
> > >         xmipsexectl                             1.0
> > >         xmipslsp                                1.0
> > > +       xw                                      1.0
> > > diff --git a/gas/testsuite/gas/riscv/x-wch-qk-xw-fail.d b/gas/testsuite/gas/riscv/x-wch-qk-xw-fail.d
> > > new file mode 100644
> > > index 00000000000..322c943e00b
> > > --- /dev/null
> > > +++ b/gas/testsuite/gas/riscv/x-wch-qk-xw-fail.d
> > > @@ -0,0 +1,3 @@
> > > +#as: -march=rv32ic_xw
> > > +#source: x-wch-qk-xw-fail.s
> > > +#error_output: x-wch-qk-xw-fail.l
> > > diff --git a/gas/testsuite/gas/riscv/x-wch-qk-xw-fail.l b/gas/testsuite/gas/riscv/x-wch-qk-xw-fail.l
> > > new file mode 100644
> > > index 00000000000..8c17e84dc24
> > > --- /dev/null
> > > +++ b/gas/testsuite/gas/riscv/x-wch-qk-xw-fail.l
> > > @@ -0,0 +1,33 @@
> > > +.*: Assembler messages:
> > > +.*: Error: unrecognized opcode `wchqk.c.lbu x8,0\(x8\)', extension `xw' required
> > > +.*: Error: unrecognized opcode `wchqk.c.sb x8,0\(x8\)', extension `xw' required
> > > +.*: Error: unrecognized opcode `wchqk.c.sb x8,-2\(x8\)', extension `xw' required
> > > +.*: Error: unrecognized opcode `wchqk.c.sb x8,0\(x7\)', extension `xw' required
> > > +.*: Error: illegal operands `wchqk.c.sb x7,0\(x8\)'
> > > +.*: Error: illegal operands `wchqk.c.sb x7,-2\(x20\)'
> > > +.*: Error: illegal operands `wchqk.c.lhu x8,1\(x8\)'
> > > +.*: Error: illegal operands `wchqk.c.lhu x8,64\(x8\)'
> > > +.*: Error: illegal operands `wchqk.c.sh x8,1\(x8\)'
> > > +.*: Error: illegal operands `wchqk.c.sh x8,64\(x8\)'
> > > +.*: Error: illegal operands `wchqk.c.sh x8,-1\(x8\)'
> > > +.*: Error: illegal operands `wchqk.c.sh x8,-2\(x8\)'
> > > +.*: Error: illegal operands `wchqk.c.sh x8,0\(x7\)'
> > > +.*: Error: illegal operands `wchqk.c.sh x7,0\(x8\)'
> > > +.*: Error: illegal operands `wchqk.c.sh x7,-2\(x20\)'
> > > +.*: Error: illegal operands `wchqk.c.lbusp x8,0\(x8\)'
> > > +.*: Error: illegal operands `wchqk.c.lbusp x8,32\(sp\)'
> > > +.*: Error: illegal operands `wchqk.c.sbsp x8,0\(x8\)'
> > > +.*: Error: illegal operands `wchqk.c.sbsp x8,32\(sp\)'
> > > +.*: Error: illegal operands `wchqk.c.sbsp x8,-2\(sp\)'
> > > +.*: Error: illegal operands `wchqk.c.sbsp x7,0\(sp\)'
> > > +.*: Error: illegal operands `wchqk.c.sbsp x7,-2\(sp\)'
> > > +.*: Error: illegal operands `wchqk.c.lhusp x8,0\(x8\)'
> > > +.*: Error: illegal operands `wchqk.c.lhusp x8,1\(sp\)'
> > > +.*: Error: illegal operands `wchqk.c.lhusp x8,32\(sp\)'
> > > +.*: Error: illegal operands `wchqk.c.shsp x8,0\(x8\)'
> > > +.*: Error: illegal operands `wchqk.c.shsp x8,1\(sp\)'
> > > +.*: Error: illegal operands `wchqk.c.shsp x8,32\(sp\)'
> > > +.*: Error: illegal operands `wchqk.c.shsp x8,-1\(sp\)'
> > > +.*: Error: illegal operands `wchqk.c.shsp x8,-2\(sp\)'
> > > +.*: Error: illegal operands `wchqk.c.shsp x7,0\(sp\)'
> > > +.*: Error: illegal operands `wchqk.c.shsp x7,-2\(sp\)'
> > > diff --git a/gas/testsuite/gas/riscv/x-wch-qk-xw-fail.s b/gas/testsuite/gas/riscv/x-wch-qk-xw-fail.s
> > > new file mode 100644
> > > index 00000000000..0f2323ffe0c
> > > --- /dev/null
> > > +++ b/gas/testsuite/gas/riscv/x-wch-qk-xw-fail.s
> > > @@ -0,0 +1,39 @@
> > > +target:
> > > +.option norvc
> > > +       wchqk.c.lbu x8, 0(x8)
> > > +       wchqk.c.sb x8, 0(x8)
> > > +.option rvc
> > > +       wchqk.c.sb x8, -2(x8)
> > > +       wchqk.c.sb x8, 0(x7)
> > > +.option arch,+xw
> > > +       wchqk.c.sb x7, 0(x8)
> > > +       wchqk.c.sb x7, -2(x20)
> > > +
> > > +       wchqk.c.lhu x8, 1(x8)
> > > +       wchqk.c.lhu x8, 64(x8)
> > > +       wchqk.c.sh x8, 1(x8)
> > > +       wchqk.c.sh x8, 64(x8)
> > > +       wchqk.c.sh x8, -1(x8)
> > > +       wchqk.c.sh x8, -2(x8)
> > > +       wchqk.c.sh x8, 0(x7)
> > > +       wchqk.c.sh x7, 0(x8)
> > > +       wchqk.c.sh x7, -2(x20)
> > > +
> > > +       wchqk.c.lbusp x8, 0(x8)
> > > +       wchqk.c.lbusp x8, 32(sp)
> > > +       wchqk.c.sbsp x8, 0(x8)
> > > +       wchqk.c.sbsp x8, 32(sp)
> > > +       wchqk.c.sbsp x8, -2(sp)
> > > +       wchqk.c.sbsp x7, 0(sp)
> > > +       wchqk.c.sbsp x7, -2(sp)
> > > +
> > > +       wchqk.c.lhusp x8, 0(x8)
> > > +       wchqk.c.lhusp x8, 1(sp)
> > > +       wchqk.c.lhusp x8, 32(sp)
> > > +       wchqk.c.shsp x8, 0(x8)
> > > +       wchqk.c.shsp x8, 1(sp)
> > > +       wchqk.c.shsp x8, 32(sp)
> > > +       wchqk.c.shsp x8, -1(sp)
> > > +       wchqk.c.shsp x8, -2(sp)
> > > +       wchqk.c.shsp x7, 0(sp)
> > > +       wchqk.c.shsp x7, -2(sp)
> > > diff --git a/gas/testsuite/gas/riscv/x-wch-qk-xw-norvc.d b/gas/testsuite/gas/riscv/x-wch-qk-xw-norvc.d
> > > new file mode 100644
> > > index 00000000000..ab6518dc1e7
> > > --- /dev/null
> > > +++ b/gas/testsuite/gas/riscv/x-wch-qk-xw-norvc.d
> > > @@ -0,0 +1,26 @@
> > > +#as: -march=rv32ic_xw
> > > +#source: x-wch-qk-xw-norvc.s
> > > +#objdump: -d -Mno-aliases
> > > +
> > > +.*:[   ]+file format .*
> > > +
> > > +
> > > +Disassembly of section .text:
> > > +
> > > +0+000 <target>:
> > > +[      ]+[0-9a-f]+:[   ]+30c0[         ]+wchqk.c.lbu[  ]+s0,5\(s1\)
> > > +[      ]+[0-9a-f]+:[   ]+3d60[         ]+wchqk.c.lbu[  ]+s0,31\(a0\)
> > > +[      ]+[0-9a-f]+:[   ]+2000[         ]+wchqk.c.lbu[  ]+s0,0\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+3000[         ]+wchqk.c.lbu[  ]+s0,1\(s0\)
> > > +
> > > +[0-9a-f]+ <norvc>:
> > > +[      ]+[0-9a-f]+:[   ]+0054c403[     ]+lbu[  ]+s0,5\(s1\)
> > > +[      ]+[0-9a-f]+:[   ]+01f54403[     ]+lbu[  ]+s0,31\(a0\)
> > > +
> > > +[0-9a-f]+ <rvc>:
> > > +[      ]+[0-9a-f]+:[   ]+0054c403[     ]+lbu[  ]+s0,5\(s1\)
> > > +[      ]+[0-9a-f]+:[   ]+01f54403[     ]+lbu[  ]+s0,31\(a0\)
> > > +
> > > +[0-9a-f]+ <rvc_xw>:
> > > +[      ]+[0-9a-f]+:[   ]+30c0[         ]+wchqk.c.lbu[  ]+s0,5\(s1\)
> > > +[      ]+[0-9a-f]+:[   ]+3d60[         ]+wchqk.c.lbu[  ]+s0,31\(a0\)
> > > diff --git a/gas/testsuite/gas/riscv/x-wch-qk-xw-norvc.s b/gas/testsuite/gas/riscv/x-wch-qk-xw-norvc.s
> > > new file mode 100644
> > > index 00000000000..73ca3ac9e06
> > > --- /dev/null
> > > +++ b/gas/testsuite/gas/riscv/x-wch-qk-xw-norvc.s
> > > @@ -0,0 +1,20 @@
> > > +target:
> > > +       lbu s0, 5(s1)
> > > +       lbu s0, 31(a0)
> > > +       wchqk.c.lbu s0, 0(s0)
> > > +       wchqk.c.lbu s0, 1(s0)
> > > +
> > > +norvc:
> > > +.option norvc
> > > +       lbu s0, 5(s1)
> > > +       lbu s0, 31(a0)
> > > +
> > > +rvc:
> > > +.option rvc
> > > +       lbu s0, 5(s1)
> > > +       lbu s0, 31(a0)
> > > +
> > > +rvc_xw:
> > > +.option arch,+xw
> > > +       lbu s0, 5(s1)
> > > +       lbu s0, 31(a0)
> > > diff --git a/gas/testsuite/gas/riscv/x-wch-qk-xw.d b/gas/testsuite/gas/riscv/x-wch-qk-xw.d
> > > new file mode 100644
> > > index 00000000000..510e04f1b47
> > > --- /dev/null
> > > +++ b/gas/testsuite/gas/riscv/x-wch-qk-xw.d
> > > @@ -0,0 +1,81 @@
> > > +#as: -march=rv32ic_xw
> > > +#source: x-wch-qk-xw.s
> > > +#objdump: -d -Mno-aliases
> > > +
> > > +.*:[   ]+file format .*
> > > +
> > > +
> > > +Disassembly of section .text:
> > > +
> > > +0+000 <implicit_xw>:
> > > +[      ]+[0-9a-f]+:[   ]+30c0[         ]+wchqk.c.lbu[  ]+s0,5\(s1\)
> > > +[      ]+[0-9a-f]+:[   ]+3d60[         ]+wchqk.c.lbu[  ]+s0,31\(a0\)
> > > +[      ]+[0-9a-f]+:[   ]+2100[         ]+wchqk.c.lbu[  ]+s0,0\(a0\)
> > > +[      ]+[0-9a-f]+:[   ]+00094403[     ]+lbu[  ]+s0,0\(s2\)
> > > +[      ]+[0-9a-f]+:[   ]+02044403[     ]+lbu[  ]+s0,32\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+b0c0[         ]+wchqk.c.sb[   ]+s0,5\(s1\)
> > > +[      ]+[0-9a-f]+:[   ]+bd60[         ]+wchqk.c.sb[   ]+s0,31\(a0\)
> > > +[      ]+[0-9a-f]+:[   ]+a100[         ]+wchqk.c.sb[   ]+s0,0\(a0\)
> > > +[      ]+[0-9a-f]+:[   ]+00890023[     ]+sb[   ]+s0,0\(s2\)
> > > +[      ]+[0-9a-f]+:[   ]+02840023[     ]+sb[   ]+s0,32\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+24a2[         ]+wchqk.c.lhu[  ]+s0,10\(s1\)
> > > +[      ]+[0-9a-f]+:[   ]+3d62[         ]+wchqk.c.lhu[  ]+s0,62\(a0\)
> > > +[      ]+[0-9a-f]+:[   ]+2102[         ]+wchqk.c.lhu[  ]+s0,0\(a0\)
> > > +[      ]+[0-9a-f]+:[   ]+00095403[     ]+lhu[  ]+s0,0\(s2\)
> > > +[      ]+[0-9a-f]+:[   ]+00145403[     ]+lhu[  ]+s0,1\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+04045403[     ]+lhu[  ]+s0,64\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+a4a2[         ]+wchqk.c.sh[   ]+s0,10\(s1\)
> > > +[      ]+[0-9a-f]+:[   ]+bd62[         ]+wchqk.c.sh[   ]+s0,62\(a0\)
> > > +[      ]+[0-9a-f]+:[   ]+a102[         ]+wchqk.c.sh[   ]+s0,0\(a0\)
> > > +[      ]+[0-9a-f]+:[   ]+00891023[     ]+sh[   ]+s0,0\(s2\)
> > > +[      ]+[0-9a-f]+:[   ]+008410a3[     ]+sh[   ]+s0,1\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+04841023[     ]+sh[   ]+s0,64\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+8010[         ]+wchqk.c.lbusp[        ]+a2,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8390[         ]+wchqk.c.lbusp[        ]+a2,7\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8790[         ]+wchqk.c.lbusp[        ]+a2,15\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+00014903[     ]+lbu[  ]+s2,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+01014403[     ]+lbu[  ]+s0,16\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8050[         ]+wchqk.c.sbsp[         ]+a2,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+83d0[         ]+wchqk.c.sbsp[         ]+a2,7\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+87d0[         ]+wchqk.c.sbsp[         ]+a2,15\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+01210023[     ]+sb[   ]+s2,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+00810823[     ]+sb[   ]+s0,16\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8030[         ]+wchqk.c.lhusp[        ]+a2,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8730[         ]+wchqk.c.lhusp[        ]+a2,14\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+87b0[         ]+wchqk.c.lhusp[        ]+a2,30\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+00015903[     ]+lhu[  ]+s2,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+00115903[     ]+lhu[  ]+s2,1\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+02015403[     ]+lhu[  ]+s0,32\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8070[         ]+wchqk.c.shsp[         ]+a2,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8770[         ]+wchqk.c.shsp[         ]+a2,14\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+87f0[         ]+wchqk.c.shsp[         ]+a2,30\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+01211023[     ]+sh[   ]+s2,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+012110a3[     ]+sh[   ]+s2,1\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+02811023[     ]+sh[   ]+s0,32\(sp\)
> > > +
> > > +[0-9a-f]+ <explicit_xw>:
> > > +[      ]+[0-9a-f]+:[   ]+2000[         ]+wchqk.c.lbu[  ]+s0,0\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+2000[         ]+wchqk.c.lbu[  ]+s0,0\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+3000[         ]+wchqk.c.lbu[  ]+s0,1\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+2800[         ]+wchqk.c.lbu[  ]+s0,16\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+a000[         ]+wchqk.c.sb[   ]+s0,0\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+a000[         ]+wchqk.c.sb[   ]+s0,0\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+a800[         ]+wchqk.c.sb[   ]+s0,16\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+2002[         ]+wchqk.c.lhu[  ]+s0,0\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+2002[         ]+wchqk.c.lhu[  ]+s0,0\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+3002[         ]+wchqk.c.lhu[  ]+s0,32\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+a002[         ]+wchqk.c.sh[   ]+s0,0\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+a002[         ]+wchqk.c.sh[   ]+s0,0\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+b002[         ]+wchqk.c.sh[   ]+s0,32\(s0\)
> > > +[      ]+[0-9a-f]+:[   ]+8000[         ]+wchqk.c.lbusp[        ]+s0,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8000[         ]+wchqk.c.lbusp[        ]+s0,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8400[         ]+wchqk.c.lbusp[        ]+s0,8\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8040[         ]+wchqk.c.sbsp[         ]+s0,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8040[         ]+wchqk.c.sbsp[         ]+s0,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8440[         ]+wchqk.c.sbsp[         ]+s0,8\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8020[         ]+wchqk.c.lhusp[        ]+s0,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8020[         ]+wchqk.c.lhusp[        ]+s0,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+80a0[         ]+wchqk.c.lhusp[        ]+s0,16\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8060[         ]+wchqk.c.shsp[         ]+s0,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+8060[         ]+wchqk.c.shsp[         ]+s0,0\(sp\)
> > > +[      ]+[0-9a-f]+:[   ]+80e0[         ]+wchqk.c.shsp[         ]+s0,16\(sp\)
> > > diff --git a/gas/testsuite/gas/riscv/x-wch-qk-xw.s b/gas/testsuite/gas/riscv/x-wch-qk-xw.s
> > > new file mode 100644
> > > index 00000000000..a85981371c6
> > > --- /dev/null
> > > +++ b/gas/testsuite/gas/riscv/x-wch-qk-xw.s
> > > @@ -0,0 +1,86 @@
> > > +implicit_xw:
> > > +       lbu s0, 5(s1)
> > > +       lbu s0, 31(a0)
> > > +       lbu s0, (a0)
> > > +       lbu s0, 0(s2)
> > > +       lbu s0, 32(s0)
> > > +
> > > +       sb s0, 5(s1)
> > > +       sb s0, 31(a0)
> > > +       sb s0, (a0)
> > > +       sb s0, 0(s2)
> > > +       sb s0, 32(s0)
> > > +
> > > +       lhu s0, 10(s1)
> > > +       lhu s0, 62(a0)
> > > +       lhu s0, (a0)
> > > +       lhu s0, 0(s2)
> > > +       lhu s0, 1(s0)
> > > +       lhu s0, 64(s0)
> > > +
> > > +       sh s0, 10(s1)
> > > +       sh s0, 62(a0)
> > > +       sh s0, (a0)
> > > +       sh s0, 0(s2)
> > > +       sh s0, 1(s0)
> > > +       sh s0, 64(s0)
> > > +
> > > +       lbu a2, (sp)
> > > +       lbu a2, 7(sp)
> > > +       lbu a2, 15(sp)
> > > +       lbu s2, 0(sp)
> > > +       lbu s0, 16(sp)
> > > +
> > > +       sb a2, (sp)
> > > +       sb a2, 7(sp)
> > > +       sb a2, 15(sp)
> > > +       sb s2, 0(sp)
> > > +       sb s0, 16(sp)
> > > +
> > > +       lhu a2, (sp)
> > > +       lhu a2, 14(sp)
> > > +       lhu a2, 30(sp)
> > > +       lhu s2, 0(sp)
> > > +       lhu s2, 1(sp)
> > > +       lhu s0, 32(sp)
> > > +
> > > +       sh a2, (sp)
> > > +       sh a2, 14(sp)
> > > +       sh a2, 30(sp)
> > > +       sh s2, 0(sp)
> > > +       sh s2, 1(sp)
> > > +       sh s0, 32(sp)
> > > +
> > > +explicit_xw:
> > > +       wchqk.c.lbu s0, (s0)
> > > +       wchqk.c.lbu s0, 0(s0)
> > > +       wchqk.c.lbu s0, 1(s0)
> > > +       wchqk.c.lbu s0, 16(s0)
> > > +
> > > +       wchqk.c.sb s0, (s0)
> > > +       wchqk.c.sb s0, 0(s0)
> > > +       wchqk.c.sb s0, 16(s0)
> > > +
> > > +       wchqk.c.lhu s0, (s0)
> > > +       wchqk.c.lhu s0, 0(s0)
> > > +       wchqk.c.lhu s0, 32(s0)
> > > +
> > > +       wchqk.c.sh s0, (s0)
> > > +       wchqk.c.sh s0, 0(s0)
> > > +       wchqk.c.sh s0, 32(s0)
> > > +
> > > +       wchqk.c.lbusp s0, (sp)
> > > +       wchqk.c.lbusp s0, 0(sp)
> > > +       wchqk.c.lbusp s0, 8(sp)
> > > +
> > > +       wchqk.c.sbsp s0, (sp)
> > > +       wchqk.c.sbsp s0, 0(sp)
> > > +       wchqk.c.sbsp s0, 8(sp)
> > > +
> > > +       wchqk.c.lhusp s0, (sp)
> > > +       wchqk.c.lhusp s0, 0(sp)
> > > +       wchqk.c.lhusp s0, 16(sp)
> > > +
> > > +       wchqk.c.shsp s0, (sp)
> > > +       wchqk.c.shsp s0, 0(sp)
> > > +       wchqk.c.shsp s0, 16(sp)
> > > diff --git a/include/ChangeLog b/include/ChangeLog
> > > index 6ef4b590930..e52dbe9e71f 100644
> > > --- a/include/ChangeLog
> > > +++ b/include/ChangeLog
> > > @@ -1,3 +1,8 @@
> > > +2025-11-23  Andy Lin <andylinpersonal@gmail.com>
> > > +
> > > +       * opcode/riscv-opc.h: Add MATCH and MASK opcode macros for XW.
> > > +       * opcode/riscv.h (riscv_insn_class): Add INSN_CLASS_XWCH_QK.
> > > +
> > >  2025-07-13  Nick Clifton  <nickc@redhat.com>
> > >
> > >         * 2.45 Branch point.
> > > diff --git a/include/opcode/riscv-opc.h b/include/opcode/riscv-opc.h
> > > index bce59c7e6f7..8f54973ed26 100644
> > > --- a/include/opcode/riscv-opc.h
> > > +++ b/include/opcode/riscv-opc.h
> > > @@ -3811,6 +3811,23 @@
> > >  /* Vendor-specific (SiFive) cease instruction.  */
> > >  #define MATCH_SF_CEASE 0x30500073
> > >  #define MASK_SF_CEASE 0xffffffff
> > > +/* Qinheng QingKe Xw instructions. */
> > > +#define MATCH_WCH_QK_C_LBU 0x2000
> > > +#define MASK_WCH_QK_C_LBU 0xe003
> > > +#define MATCH_WCH_QK_C_LHU 0x2002
> > > +#define MASK_WCH_QK_C_LHU 0xe003
> > > +#define MATCH_WCH_QK_C_SB 0xa000
> > > +#define MASK_WCH_QK_C_SB 0xe003
> > > +#define MATCH_WCH_QK_C_SH 0xa002
> > > +#define MASK_WCH_QK_C_SH 0xe003
> > > +#define MATCH_WCH_QK_C_LBUSP 0x8000
> > > +#define MASK_WCH_QK_C_LBUSP 0xf863
> > > +#define MATCH_WCH_QK_C_LHUSP 0x8020
> > > +#define MASK_WCH_QK_C_LHUSP 0xf863
> > > +#define MATCH_WCH_QK_C_SBSP 0x8040
> > > +#define MASK_WCH_QK_C_SBSP 0xf863
> > > +#define MATCH_WCH_QK_C_SHSP 0x8060
> > > +#define MASK_WCH_QK_C_SHSP 0xf863
> > >  /* SiFive custom int8 matrix-multiply instruction.  */
> > >  #define MATCH_SFVQMACCU4X8X4 0xf200205b
> > >  #define MASK_SFVQMACCU4X8X4 0xfe00707f
> > > @@ -5022,6 +5039,15 @@ DECLARE_INSN(mips_ehb, MATCH_MIPS_EHB, MASK_MIPS_EHB)
> > >  DECLARE_INSN(mips_ihb, MATCH_MIPS_IHB, MASK_MIPS_IHB)
> > >  DECLARE_INSN(mips_pause, MATCH_MIPS_PAUSE, MASK_MIPS_PAUSE)
> > >  DECLARE_INSN(mips_pref, MATCH_MIPS_PREF, MASK_MIPS_PREF)
> > > +/* Vendor-specific (Qinheng QingKe Xw) instructions. */
> > > +DECLARE_INSN(qk_c_lbu, MATCH_WCH_QK_C_LBU, MASK_WCH_QK_C_LBU)
> > > +DECLARE_INSN(qk_c_lhu, MATCH_WCH_QK_C_LHU, MASK_WCH_QK_C_LHU)
> > > +DECLARE_INSN(qk_c_sb, MATCH_WCH_QK_C_SB, MASK_WCH_QK_C_SB)
> > > +DECLARE_INSN(qk_c_sh, MATCH_WCH_QK_C_SH, MASK_WCH_QK_C_SH)
> > > +DECLARE_INSN(qk_c_lbusp, MATCH_WCH_QK_C_LBUSP, MASK_WCH_QK_C_LBUSP)
> > > +DECLARE_INSN(qk_c_lhusp, MATCH_WCH_QK_C_LHUSP, MASK_WCH_QK_C_LHUSP)
> > > +DECLARE_INSN(qk_c_sbsp, MATCH_WCH_QK_C_SBSP, MASK_WCH_QK_C_SBSP)
> > > +DECLARE_INSN(qk_c_shsp, MATCH_WCH_QK_C_SHSP, MASK_WCH_QK_C_SHSP)
> > >  #endif /* DECLARE_INSN */
> > >  #ifdef DECLARE_CSR
> > >  /* Unprivileged Counter/Timers CSRs.  */
> > > diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
> > > index 858fcce6871..e0b6033ca5e 100644
> > > --- a/include/opcode/riscv.h
> > > +++ b/include/opcode/riscv.h
> > > @@ -141,6 +141,15 @@ static inline unsigned int riscv_insn_length (insn_t insn)
> > >    ((RV_X(x, 25, 2) << 5) | (RV_X(x, 9, 3) << 2))
> > >  #define EXTRACT_MIPS_SDP_IMM(x) \
> > >    ((RV_X(x, 25, 2) << 5) | (RV_X(x, 10, 2) << 3))
> > > +/* Vendor-specific (Qinheng QingKe Xw) extract macros.  */
> > > +#define EXTRACT_WCH_QK_UIMM5(x) \
> > > +  ((RV_X(x, 12, 1)) | (RV_X(x, 10, 2) << 3) | (RV_X(x, 5, 2) << 1))
> > > +#define EXTRACT_WCH_QK_UIMM6_LSB0(x) \
> > > +  ((RV_X(x, 10, 3) << 3) | (RV_X(x, 5, 2) << 1))
> > > +#define EXTRACT_WCH_QK_UIMM4(x) \
> > > +  (RV_X(x, 7, 4))
> > > +#define EXTRACT_WCH_QK_UIMM5_LSB0(x) \
> > > +  ((RV_X(x, 8, 3) << 1) | (RV_X(x, 7, 1) << 4))
> > >
> > >  #define ENCODE_ITYPE_IMM(x) \
> > >    (RV_X(x, 0, 12) << 20)
> > > @@ -218,6 +227,15 @@ static inline unsigned int riscv_insn_length (insn_t insn)
> > >    ((RV_X(x, 5, 2) << 25) | (RV_X(x, 2, 3) << 9))
> > >  #define ENCODE_MIPS_SDP_IMM(x) \
> > >    ((RV_X(x, 5, 2) << 25) | (RV_X(x, 3, 2) << 10))
> > > +/* Vendor-specific (Qinheng QingKe Xw) encode macros.  */
> > > +#define ENCODE_WCH_QK_UIMM5(x) \
> > > +  ((RV_X(x, 0, 1) << 12) | (RV_X(x, 3, 2) << 10) | (RV_X(x, 1, 2) << 5))
> > > +#define ENCODE_WCH_QK_UIMM6_LSB0(x) \
> > > +  ((RV_X(x, 5, 1) << 12) | (RV_X(x, 3, 2) << 10) | (RV_X(x, 1, 2) << 5))
> > > +#define ENCODE_WCH_QK_UIMM4(x) \
> > > +  (RV_X(x, 0, 4) << 7)
> > > +#define ENCODE_WCH_QK_UIMM5_LSB0(x) \
> > > +  ((RV_X(x, 1, 3) << 8) | (RV_X(x, 4, 1) << 7))
> > >
> > >  #define VALID_ITYPE_IMM(x) (EXTRACT_ITYPE_IMM(ENCODE_ITYPE_IMM(x)) == (x))
> > >  #define VALID_STYPE_IMM(x) (EXTRACT_STYPE_IMM(ENCODE_STYPE_IMM(x)) == (x))
> > > @@ -246,6 +264,11 @@ static inline unsigned int riscv_insn_length (insn_t insn)
> > >  #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))
> > > +/* Vendor-specific (Qinheng QingKe Xw) validation macros.  */
> > > +#define VALID_WCH_QK_UIMM5(x) (EXTRACT_WCH_QK_UIMM5(ENCODE_WCH_QK_UIMM5(x)) == (x))
> > > +#define VALID_WCH_QK_UIMM6_LSB0(x) (EXTRACT_WCH_QK_UIMM6_LSB0(ENCODE_WCH_QK_UIMM6_LSB0(x)) == (x))
> > > +#define VALID_WCH_QK_UIMM4(x) (EXTRACT_WCH_QK_UIMM4(ENCODE_WCH_QK_UIMM4(x)) == (x))
> > > +#define VALID_WCH_QK_UIMM5_LSB0(x) (EXTRACT_WCH_QK_UIMM5_LSB0(ENCODE_WCH_QK_UIMM5_LSB0(x)) == (x))
> > >
> > >  #define RISCV_RTYPE(insn, rd, rs1, rs2) \
> > >    ((MATCH_ ## insn) | ((rd) << OP_SH_RD) | ((rs1) << OP_SH_RS1) | ((rs2) << OP_SH_RS2))
> > > @@ -606,6 +629,7 @@ enum riscv_insn_class
> > >    INSN_CLASS_XMIPSCMOV,
> > >    INSN_CLASS_XMIPSEXECTL,
> > >    INSN_CLASS_XMIPSLSP,
> > > +  INSN_CLASS_XWCH_QK,
> > >  };
> > >
> > >  /* This structure holds information for a particular instruction.  */
> > > diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog
> > > index 13659e317eb..dc5839b83d5 100644
> > > --- a/opcodes/ChangeLog
> > > +++ b/opcodes/ChangeLog
> > > @@ -1,3 +1,8 @@
> > > +2025-11-23  Andy Lin <andylinpersonal@gmail.com>
> > > +
> > > +       * riscv-opc.c (riscv_opcodes): Add XW opcodes and aliases.
> > > +       * riscv-dis.c (print_insn_args): Add XW opcode to disassembler.
> > > +
> > >  2025-07-13  Nick Clifton  <nickc@redhat.com>
> > >
> > >         * 2.45 Branch point.
> > > diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c
> > > index fa2d44af28f..4e92dcd41ac 100644
> > > --- a/opcodes/riscv-dis.c
> > > +++ b/opcodes/riscv-dis.c
> > > @@ -917,6 +917,29 @@ print_insn_args (const char *oparg, insn_t l, bfd_vma pc, disassemble_info *info
> > >                   goto undefined_modifier;
> > >                 }
> > >               break;
> > > +           case 'w': /* Vendor-specific (Qinghen QingKe Xw) operands.  */
> > > +             switch (*++oparg)
> > > +               {
> > > +               case 'b':
> > > +                 print (info->stream, dis_style_immediate, "%d",
> > > +                        (unsigned)EXTRACT_WCH_QK_UIMM5 (l));
> > > +                 break;
> > > +               case '6':
> > > +                 print (info->stream, dis_style_immediate, "%d",
> > > +                        (unsigned)EXTRACT_WCH_QK_UIMM6_LSB0 (l));
> > > +                 break;
> > > +               case '4':
> > > +                 print (info->stream, dis_style_immediate, "%d",
> > > +                        (unsigned)EXTRACT_WCH_QK_UIMM4 (l));
> > > +                 break;
> > > +               case '5':
> > > +                 print (info->stream, dis_style_immediate, "%d",
> > > +                        (unsigned)EXTRACT_WCH_QK_UIMM5_LSB0 (l));
> > > +                 break;
> > > +               default:
> > > +                 goto undefined_modifier;
> > > +               }
> > > +             break;
> > >             default:
> > >               goto undefined_modifier;
> > >             }
> > > diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
> > > index e6fe5e9afba..69fe23fd553 100644
> > > --- a/opcodes/riscv-opc.c
> > > +++ b/opcodes/riscv-opc.c
> > > @@ -589,12 +589,16 @@ const struct riscv_opcode riscv_opcodes[] =
> > >  {"lb",          0, INSN_CLASS_I, "d,o(s)",    MATCH_LB, MASK_LB, match_opcode, INSN_DREF|INSN_1_BYTE },
> > >  {"lb",          0, INSN_CLASS_I, "d,A",       0, (int) M_Lx, match_rd_nonzero, INSN_MACRO },
> > >  {"lbu",         0, INSN_CLASS_ZCB, "Ct,Wcb(Cs)", MATCH_C_LBU, MASK_C_LBU, match_opcode, INSN_ALIAS|INSN_DREF|INSN_1_BYTE },
> > > +{"lbu",        32, INSN_CLASS_XWCH_QK, "Ct,Xw4(Cc)", MATCH_WCH_QK_C_LBUSP, MASK_WCH_QK_C_LBUSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_1_BYTE },
> > > +{"lbu",        32, INSN_CLASS_XWCH_QK, "Ct,Xwb(Cs)", MATCH_WCH_QK_C_LBU, MASK_WCH_QK_C_LBU, match_opcode, INSN_ALIAS|INSN_DREF|INSN_1_BYTE },
> > >  {"lbu",         0, INSN_CLASS_I, "d,o(s)",    MATCH_LBU, MASK_LBU, match_opcode, INSN_DREF|INSN_1_BYTE },
> > >  {"lbu",         0, INSN_CLASS_I, "d,A",       0, (int) M_Lx, match_rd_nonzero, INSN_MACRO },
> > >  {"lh",          0, INSN_CLASS_ZCB, "Ct,Wch(Cs)", MATCH_C_LH, MASK_C_LH, match_opcode, INSN_ALIAS|INSN_DREF|INSN_2_BYTE },
> > >  {"lh",          0, INSN_CLASS_I, "d,o(s)",    MATCH_LH, MASK_LH, match_opcode, INSN_DREF|INSN_2_BYTE },
> > >  {"lh",          0, INSN_CLASS_I, "d,A",       0, (int) M_Lx, match_rd_nonzero, INSN_MACRO },
> > >  {"lhu",         0, INSN_CLASS_ZCB, "Ct,Wch(Cs)", MATCH_C_LHU, MASK_C_LHU, match_opcode, INSN_ALIAS|INSN_DREF|INSN_2_BYTE },
> > > +{"lhu",        32, INSN_CLASS_XWCH_QK, "Ct,Xw5(Cc)", MATCH_WCH_QK_C_LHUSP, MASK_WCH_QK_C_LHUSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_2_BYTE },
> > > +{"lhu",        32, INSN_CLASS_XWCH_QK, "Ct,Xw6(Cs)", MATCH_WCH_QK_C_LHU, MASK_WCH_QK_C_LHU, match_opcode, INSN_ALIAS|INSN_DREF|INSN_2_BYTE },
> > >  {"lhu",         0, INSN_CLASS_I, "d,o(s)",    MATCH_LHU, MASK_LHU, match_opcode, INSN_DREF|INSN_2_BYTE },
> > >  {"lhu",         0, INSN_CLASS_I, "d,A",       0, (int) M_Lx, match_rd_nonzero, INSN_MACRO },
> > >  {"lw",          0, INSN_CLASS_ZCA, "d,Cm(Cc)",  MATCH_C_LWSP, MASK_C_LWSP, match_rd_nonzero, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
> > > @@ -626,9 +630,13 @@ const struct riscv_opcode riscv_opcodes[] =
> > >  {"sgt",         0, INSN_CLASS_I, "d,t,s",     MATCH_SLT, MASK_SLT, match_opcode, INSN_ALIAS },
> > >  {"sgtu",        0, INSN_CLASS_I, "d,t,s",     MATCH_SLTU, MASK_SLTU, match_opcode, INSN_ALIAS },
> > >  {"sb",          0, INSN_CLASS_ZCB, "Ct,Wcb(Cs)", MATCH_C_SB, MASK_C_SB, match_opcode, INSN_DREF|INSN_1_BYTE|INSN_ALIAS },
> > > +{"sb",         32, INSN_CLASS_XWCH_QK, "Ct,Xw4(Cc)", MATCH_WCH_QK_C_SBSP, MASK_WCH_QK_C_SBSP, match_opcode, INSN_DREF|INSN_1_BYTE|INSN_ALIAS },
> > > +{"sb",         32, INSN_CLASS_XWCH_QK, "Ct,Xwb(Cs)", MATCH_WCH_QK_C_SB, MASK_WCH_QK_C_SB, match_opcode, INSN_DREF|INSN_1_BYTE|INSN_ALIAS },
> > >  {"sb",          0, INSN_CLASS_I, "t,q(s)",    MATCH_SB, MASK_SB, match_opcode, INSN_DREF|INSN_1_BYTE },
> > >  {"sb",          0, INSN_CLASS_I, "t,A,s",     0, (int) M_Sx_FSx, match_rs1_nonzero, INSN_MACRO },
> > >  {"sh",          0, INSN_CLASS_ZCB, "Ct,Wch(Cs)", MATCH_C_SH, MASK_C_SH, match_opcode, INSN_DREF|INSN_2_BYTE|INSN_ALIAS },
> > > +{"sh",         32, INSN_CLASS_XWCH_QK, "Ct,Xw5(Cc)", MATCH_WCH_QK_C_SHSP, MASK_WCH_QK_C_SHSP, match_opcode, INSN_DREF|INSN_2_BYTE|INSN_ALIAS },
> > > +{"sh",         32, INSN_CLASS_XWCH_QK, "Ct,Xw6(Cs)", MATCH_WCH_QK_C_SH, MASK_WCH_QK_C_SH, match_opcode, INSN_DREF|INSN_2_BYTE|INSN_ALIAS },
> > >  {"sh",          0, INSN_CLASS_I, "t,q(s)",    MATCH_SH, MASK_SH, match_opcode, INSN_DREF|INSN_2_BYTE },
> > >  {"sh",          0, INSN_CLASS_I, "t,A,s",     0, (int) M_Sx_FSx, match_rs1_nonzero, INSN_MACRO },
> > >  {"sw",          0, INSN_CLASS_ZCA, "CV,CM(Cc)", MATCH_C_SWSP, MASK_C_SWSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
> > > @@ -3579,6 +3587,16 @@ const struct riscv_opcode riscv_opcodes[] =
> > >  {"mips.sdp", 0, INSN_CLASS_XMIPSLSP, "t,r,Xm^(s)", MATCH_MIPS_SDP, MASK_MIPS_SDP, match_opcode, 0 },
> > >  {"mips.swp", 0, INSN_CLASS_XMIPSLSP, "t,r,Xm&(s)", MATCH_MIPS_SWP, MASK_MIPS_SWP, match_opcode, 0 },
> > >
> > > +/* Qinghen QingKe Xw instructions. */
> > > +{"wchqk.c.lbu",   32, INSN_CLASS_XWCH_QK, "Ct,Xwb(Cs)", MATCH_WCH_QK_C_LBU,   MASK_WCH_QK_C_LBU,   match_opcode, INSN_DREF|INSN_1_BYTE },
> > > +{"wchqk.c.lhu",   32, INSN_CLASS_XWCH_QK, "Ct,Xw6(Cs)", MATCH_WCH_QK_C_LHU,   MASK_WCH_QK_C_LHU,   match_opcode, INSN_DREF|INSN_2_BYTE },
> > > +{"wchqk.c.sb",    32, INSN_CLASS_XWCH_QK, "Ct,Xwb(Cs)", MATCH_WCH_QK_C_SB,    MASK_WCH_QK_C_SB,    match_opcode, INSN_DREF|INSN_1_BYTE },
> > > +{"wchqk.c.sh",    32, INSN_CLASS_XWCH_QK, "Ct,Xw6(Cs)", MATCH_WCH_QK_C_SH,    MASK_WCH_QK_C_SH,    match_opcode, INSN_DREF|INSN_2_BYTE },
> > > +{"wchqk.c.lbusp", 32, INSN_CLASS_XWCH_QK, "Ct,Xw4(Cc)", MATCH_WCH_QK_C_LBUSP, MASK_WCH_QK_C_LBUSP, match_opcode, INSN_DREF|INSN_1_BYTE },
> > > +{"wchqk.c.lhusp", 32, INSN_CLASS_XWCH_QK, "Ct,Xw5(Cc)", MATCH_WCH_QK_C_LHUSP, MASK_WCH_QK_C_LHUSP, match_opcode, INSN_DREF|INSN_2_BYTE },
> > > +{"wchqk.c.sbsp",  32, INSN_CLASS_XWCH_QK, "Ct,Xw4(Cc)", MATCH_WCH_QK_C_SBSP,  MASK_WCH_QK_C_SBSP,  match_opcode, INSN_DREF|INSN_1_BYTE },
> > > +{"wchqk.c.shsp",  32, INSN_CLASS_XWCH_QK, "Ct,Xw5(Cc)", MATCH_WCH_QK_C_SHSP,  MASK_WCH_QK_C_SHSP,  match_opcode, INSN_DREF|INSN_2_BYTE },
> > > +
> > >  /* Terminate the list.  */
> > >  {0, 0, INSN_CLASS_NONE, 0, 0, 0, 0, 0}
> > >  };
> > > --
> > > 2.43.0
> > >


More information about the Binutils mailing list