RISC-V: Clarufy the behavior when extensions have superset.

Message ID 20241114123851.66071-1-nelson@rivosinc.com
State New
Headers
Series RISC-V: Clarufy the behavior when extensions have superset. |

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-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Nelson Chu Nov. 14, 2024, 12:38 p.m. UTC
  If extensions have superset, then which means the superset includes those
extensions.  We used to imply those extensions for the superset, and also
keep the superset since the canonical order form.  For example,

* m includes zmmul, m implies zmmul
* b includes zba, zbb and zbs, and b also equals to zba + zbb + zbs, so b
  implies zba, zbb and zbs

For all include rules,
m includes zmmul
zvbb includes zvkb
zhinx includes zhinxmin
zfh includes zfhmin

For all equal rules,
a equals to zaamo + zalrsc
b equals to zba + zbb + zbs
zks equals to zbkb + zbkc + zbkx + zksed + zksh
zkn equals to zbkb + zbkc + zbkx + zkne + zknd + zknh
zk equals to zkn + zkr + zkt
zvkn equals to zvkb + zvkt + zvkned + zvknhb
zvks equals to zvkb + zvkt + zvksed + zvksh
zvkng equals to zvkn + zvkg
zvknc equals to zvkn + zvbc
zvksg equals to zvks + zvkg
zvksc equals to zvks + zvbc
c equals to zca + zcf (if rv32 and f) + zcd (if d)

g is a super special case, so skip it here.

Considering ".option arch, -extension [,...,-extension_n]" removes the
extension which,
1. A includes B;
   Remove A should also remove B;
   Remove B is useless since we don't have any extensions to represent A - B
2. A equals to B + C + ...;
   Remove A should also remove B, C and ...;
   Remove B should also remove A, but keep C and ...;
   Remove C should also remove A, but keep B and ...;
   Remove B ansd C should also remove A, but keep ...;
3. A implies B;
   Remove B is useless, since A still imply B;
   Remove A doesn't affect B, since B may still be implied by others;

The rule 3 works for the current toolchain, so this patch mainly tries to fix
the rule 1 and 2, to keep the removing feature still work.  The rules for c are
really complicated now because c represents different combinations in different
situations, so please see the all details in the option-arch-superset testsuite.

bfd/
	* elfxx-riscv.c (check_implicit_always): Updated since check_func of
	riscv_implicit_subset is changed.
	(check_implicit_for_i): Likewise, also avoid segfault by NULL intputs.
	(check_implicit_for_c_zcf): New function, only add c when rv32 and f.
	(check_implicit_for_c_zcd): New function, only add c when d.
	(struct riscv_implicit_subset): Updated check_func.  Added new enum to
	represent new imply types, which are normal imply, include, or equal.
	(riscv_implicit_subsets): Added imply rule for c, and also updated all
	imlpy types.
	(riscv_parse_extract_subset): New function to extract extension and its
	versions from the input string.  This is the repeated code in the
	riscv_parse_extensions, riscv_update_subset1 and riscv_update_superset.
	(riscv_parse_extensions): Updated to call riscv_parse_extract_subset.
	(riscv_update_subset1): Likewise.  Remove all extensions if their
	superset is removed.  Also reports error when adding and removing
	extensions at the same time.
	(riscv_update_superset): New function.  Remove the superset if its
	sub-extension is removed.
	(riscv_update_subset): Updated.
	(riscv_multi_subset_supports): Don't check the superset if it has
	equivalent extensions.  Only INSN_CLASS of C need to be fixed.
	(riscv_multi_subset_supports_ext): Just report the included and
	equivalent extensions rather than the superset.
gas/
	* config/tc-riscv.c (riscv_set_arch): Only need to check the zca for
	riscv_set_rvc.
	(s_riscv_option): Likewise.
	* testsuite/gas/riscv/attribute-10.d: Updated since new imply rule of c.
	* testsuite/gas/riscv/imply.d: Likewise.
	* testsuite/gas/riscv/imply.s: Likewise.
	* testsuite/gas/riscv/mapping-symbols.d: Likewise.
	* testsuite/gas/riscv/march-ok-reorder.d: Likewise.
	* testsuite/gas/riscv/option-arch-attr.d: Likewise.
	* testsuite/gas/riscv/option-arch-dis.d: Likewise.
	* testsuite/gas/riscv/option-arch-fail.l: Updated since the error
	messag formats have been unified in riscv_parse_extract_subset.  Also
	report error when adding and removing extensions at the same time.
	* testsuite/gas/riscv/option-arch-fail.s: Likewise.
	* testsuite/gas/riscv/option-arch.s: Likewise.
	* testsuite/gas/riscv/m-ext-fail-noarch-64.l: Updated since the change
	in riscv_multi_subset_supports_ext.
	* testsuite/gas/riscv/option-arch-superset.d: New testcase of superset.
	* testsuite/gas/riscv/option-arch-superset.s: Likewise.
include/
	* opcode/riscv.h: Changed INSN_CLASS_C, INSN_CLASS_F_AND_C,
	INSN_CLASS_D_AND_C and INSN_CLASS_ZIHINTNTL_AND_C to INSN_CLASS_ZCA,
	INSN_CLASS_ZCF, INSN_CLASS_ZCD and INSN_CLASS_ZIHINTNTL_AND_ZCA.
opcodes/
	* riscv-opc.c: Updated since INSN_CLASS for C are changed.
---
 bfd/elfxx-riscv.c                             | 629 +++++++++---------
 gas/config/tc-riscv.c                         |   6 +-
 gas/testsuite/gas/riscv/attribute-10.d        |   2 +-
 gas/testsuite/gas/riscv/imply.d               |   5 +
 gas/testsuite/gas/riscv/imply.s               |  18 +-
 .../gas/riscv/m-ext-fail-noarch-64.l          |  10 +-
 gas/testsuite/gas/riscv/mapping-symbols.d     |  22 +-
 gas/testsuite/gas/riscv/march-ok-reorder.d    |   2 +-
 gas/testsuite/gas/riscv/option-arch-attr.d    |   2 +-
 gas/testsuite/gas/riscv/option-arch-dis.d     |   6 +-
 gas/testsuite/gas/riscv/option-arch-fail.l    |  15 +-
 gas/testsuite/gas/riscv/option-arch-fail.s    |   3 +-
 .../gas/riscv/option-arch-superset.d          | 154 +++++
 .../gas/riscv/option-arch-superset.s          | 180 +++++
 gas/testsuite/gas/riscv/option-arch.s         |   3 +-
 include/opcode/riscv.h                        |   8 +-
 opcodes/riscv-opc.c                           | 294 ++++----
 17 files changed, 869 insertions(+), 490 deletions(-)
 create mode 100644 gas/testsuite/gas/riscv/option-arch-superset.d
 create mode 100644 gas/testsuite/gas/riscv/option-arch-superset.s
  

Comments

Jan Beulich Nov. 14, 2024, 1 p.m. UTC | #1
On 14.11.2024 13:38, Nelson Chu wrote:
> If extensions have superset, then which means the superset includes those
> extensions.  We used to imply those extensions for the superset, and also
> keep the superset since the canonical order form.  For example,
> 
> * m includes zmmul, m implies zmmul
> * b includes zba, zbb and zbs, and b also equals to zba + zbb + zbs, so b
>   implies zba, zbb and zbs
> 
> For all include rules,
> m includes zmmul
> zvbb includes zvkb
> zhinx includes zhinxmin
> zfh includes zfhmin
> 
> For all equal rules,
> a equals to zaamo + zalrsc
> b equals to zba + zbb + zbs
> zks equals to zbkb + zbkc + zbkx + zksed + zksh
> zkn equals to zbkb + zbkc + zbkx + zkne + zknd + zknh
> zk equals to zkn + zkr + zkt
> zvkn equals to zvkb + zvkt + zvkned + zvknhb
> zvks equals to zvkb + zvkt + zvksed + zvksh
> zvkng equals to zvkn + zvkg
> zvknc equals to zvkn + zvbc
> zvksg equals to zvks + zvkg
> zvksc equals to zvks + zvbc
> c equals to zca + zcf (if rv32 and f) + zcd (if d)
> 
> g is a super special case, so skip it here.
> 
> Considering ".option arch, -extension [,...,-extension_n]" removes the
> extension which,
> 1. A includes B;
>    Remove A should also remove B;
>    Remove B is useless since we don't have any extensions to represent A - B
> 2. A equals to B + C + ...;
>    Remove A should also remove B, C and ...;
>    Remove B should also remove A, but keep C and ...;
>    Remove C should also remove A, but keep B and ...;
>    Remove B ansd C should also remove A, but keep ...;

I agree with 2, but that imo contradicts 1: Remove B should also remove A,
as A = B + <unnamed>. The two still aren't mere aliases of one another, as
behavior is different when they're added to the permitted extensions.

(Otherwise, at the very least, a removal that doesn't remove anything
should at least have a diagnostic issued.)

> 3. A implies B;
>    Remove B is useless, since A still imply B;
>    Remove A doesn't affect B, since B may still be implied by others;

I'm afraid I didn't really get what the difference is between "implies"
and "includes". In particular, related to the 2nd sentence, in the
summary at the top I can't spot any case of two distinct extensions
implying the same more narrow one. Imo, if anything this 2nd rule
should be "Remove A doesn't affect B, if B is still implied by another
active one". Plus that (in your or my form) would still be similarly
true for "included".

Jan
  
Nelson Chu Nov. 19, 2024, 5:37 a.m. UTC | #2
On Thu, Nov 14, 2024 at 9:00 PM Jan Beulich <jbeulich@suse.com> wrote:

> On 14.11.2024 13:38, Nelson Chu wrote:
> > If extensions have superset, then which means the superset includes those
> > extensions.  We used to imply those extensions for the superset, and also
> > keep the superset since the canonical order form.  For example,
> >
> > * m includes zmmul, m implies zmmul
> > * b includes zba, zbb and zbs, and b also equals to zba + zbb + zbs, so b
> >   implies zba, zbb and zbs
> >
> > For all include rules,
> > m includes zmmul
> > zvbb includes zvkb
> > zhinx includes zhinxmin
> > zfh includes zfhmin
> >
> > For all equal rules,
> > a equals to zaamo + zalrsc
> > b equals to zba + zbb + zbs
> > zks equals to zbkb + zbkc + zbkx + zksed + zksh
> > zkn equals to zbkb + zbkc + zbkx + zkne + zknd + zknh
> > zk equals to zkn + zkr + zkt
> > zvkn equals to zvkb + zvkt + zvkned + zvknhb
> > zvks equals to zvkb + zvkt + zvksed + zvksh
> > zvkng equals to zvkn + zvkg
> > zvknc equals to zvkn + zvbc
> > zvksg equals to zvks + zvkg
> > zvksc equals to zvks + zvbc
> > c equals to zca + zcf (if rv32 and f) + zcd (if d)
> >
> > g is a super special case, so skip it here.
> >
> > Considering ".option arch, -extension [,...,-extension_n]" removes the
> > extension which,
> > 1. A includes B;
> >    Remove A should also remove B;
> >    Remove B is useless since we don't have any extensions to represent A
> - B
> > 2. A equals to B + C + ...;
> >    Remove A should also remove B, C and ...;
> >    Remove B should also remove A, but keep C and ...;
> >    Remove C should also remove A, but keep B and ...;
> >    Remove B ansd C should also remove A, but keep ...;
>
> I agree with 2, but that imo contradicts 1: Remove B should also remove A,
> as A = B + <unnamed>. The two still aren't mere aliases of one another, as
> behavior is different when they're added to the permitted extensions.
>
> (Otherwise, at the very least, a removal that doesn't remove anything
> should at least have a diagnostic issued.)
>

Sounds good, at least a warning is necessary, thanks.


> > 3. A implies B;
> >    Remove B is useless, since A still imply B;
> >    Remove A doesn't affect B, since B may still be implied by others;
>
> I'm afraid I didn't really get what the difference is between "implies"
> and "includes". In particular, related to the 2nd sentence, in the
> summary at the top I can't spot any case of two distinct extensions
> implying the same more narrow one. Imo, if anything this 2nd rule
> should be "Remove A doesn't affect B, if B is still implied by another
> active one". Plus that (in your or my form) would still be similarly
> true for "included".
>

I think "includes" here is more like a large extension (superset) which is
split into small ones (subsets);

The "implies" originally means an extension generally needs others, or said
they are best used together, so toolchain helps add these implies for
convenience.  These extensions are basically different, so they are not the
same as includes.  For example, f implies zicsr was because f has some
floating csrs, so f probably will need zicsr instructions to access those
csrs, though it seems still work if not enabling zicsr.  Compared to
"imples", the "requires" is more mandatory, it is more inclined to "must".
But for now the implementation of "implies" is also mandatory and cannot be
disabled, so maybe it isn't no longer important to distinguish between
"implies" and "requires".  Anyway, if an extension
needs/requires/...whatever... some extensions, toolchain will add these
"implies" to make sure things goes well.

As for why we add those "included" as "implies", and keep the superset
before...  No matter it is because of the convenience in practice, or the
canonical order form, there is no difference when only "adding" extensions,
but have some troubles when supporting "removing"...

I think Andrew and Kito can explain these terms better than I can, so hope
they will see and have time to reply to something ;)

Thanks
Nelson
  

Patch

diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index 4b48d8ee9f0..5ee9fe73940 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -1148,7 +1148,8 @@  riscv_elf_ignore_reloc (bfd *abfd ATTRIBUTE_UNUSED,
 /* Always add implicit extensions for the SUBSET.  */
 
 static bool
-check_implicit_always (riscv_subset_t *subset ATTRIBUTE_UNUSED)
+check_implicit_always (riscv_parse_subset_t *rps ATTRIBUTE_UNUSED,
+		       riscv_subset_t *subset ATTRIBUTE_UNUSED)
 {
   return true;
 }
@@ -1156,11 +1157,35 @@  check_implicit_always (riscv_subset_t *subset ATTRIBUTE_UNUSED)
 /* Add implicit extensions only when the version of SUBSET less than 2.1.  */
 
 static bool
-check_implicit_for_i (riscv_subset_t *subset)
+check_implicit_for_i (riscv_parse_subset_t *rps ATTRIBUTE_UNUSED,
+		      riscv_subset_t *subset)
 {
-  return (subset->major_version < 2
-	  || (subset->major_version == 2
-	      && subset->minor_version < 1));
+  return (subset != NULL
+	  && (subset->major_version < 2
+	      || (subset->major_version == 2
+		  && subset->minor_version < 1)));
+}
+
+/* Add implicit extension only when rv32 and f existing.  */
+
+static bool
+check_implicit_for_c_zcf (riscv_parse_subset_t *rps,
+			  riscv_subset_t *subset ATTRIBUTE_UNUSED)
+{
+  return (rps != NULL
+	  && rps->xlen != NULL
+	  && *rps->xlen == 32
+	  && riscv_subset_supports (rps, "f"));
+}
+
+/* Add implicit extension only when d existing.  */
+
+static bool
+check_implicit_for_c_zcd (riscv_parse_subset_t *rps,
+			  riscv_subset_t *subset ATTRIBUTE_UNUSED)
+{
+  return (rps != NULL
+	  && riscv_subset_supports (rps, "d"));
 }
 
 /* Record all implicit information for the subsets.  */
@@ -1169,108 +1194,114 @@  struct riscv_implicit_subset
   const char *ext;
   const char *implicit_exts;
   /* A function to determine if we need to add the implicit subsets.  */
-  bool (*check_func) (riscv_subset_t *);
+  bool (*check_func) (riscv_parse_subset_t *rps, riscv_subset_t *);
+  enum implicit_type {IMPLY, INCLUDE, EQUAL} type;
 };
 /* Please added in order since this table is only run once time.  */
 static struct riscv_implicit_subset riscv_implicit_subsets[] =
 {
-  {"g", "+i,+m,+a,+f,+d,+zicsr,+zifencei", check_implicit_always},
-  {"e", "+i", check_implicit_always},
-  {"i", "+zicsr,+zifencei", check_implicit_for_i},
-  {"zicntr", "+zicsr", check_implicit_always},
-  {"zihpm", "+zicsr", check_implicit_always},
-
-  {"m", "+zmmul", check_implicit_always},
-
-  {"zabha", "+zaamo", check_implicit_always},
-  {"zacas", "+zaamo", check_implicit_always},
-  {"a", "+zaamo,+zalrsc", check_implicit_always},
-
-  {"xsfvcp", "+zve32x", check_implicit_always},
-  {"v", "+zve64d,+zvl128b", check_implicit_always},
-  {"zvfh", "+zvfhmin,+zfhmin", check_implicit_always},
-  {"zvfhmin", "+zve32f", check_implicit_always},
-  {"zvfbfwma", "+zve32f,+zfbfmin", check_implicit_always},
-  {"zvfbfmin", "+zve32f", check_implicit_always},
-  {"zve64d", "+d,+zve64f", check_implicit_always},
-  {"zve64f", "+zve32f,+zve64x,+zvl64b", check_implicit_always},
-  {"zve32f", "+f,+zve32x,+zvl32b", check_implicit_always},
-  {"zve64x", "+zve32x,+zvl64b", check_implicit_always},
-  {"zve32x", "+zvl32b,+zicsr", check_implicit_always},
-  {"zvl65536b", "+zvl32768b", check_implicit_always},
-  {"zvl32768b", "+zvl16384b", check_implicit_always},
-  {"zvl16384b", "+zvl8192b", check_implicit_always},
-  {"zvl8192b", "+zvl4096b", check_implicit_always},
-  {"zvl4096b", "+zvl2048b", check_implicit_always},
-  {"zvl2048b", "+zvl1024b", check_implicit_always},
-  {"zvl1024b", "+zvl512b", check_implicit_always},
-  {"zvl512b", "+zvl256b", check_implicit_always},
-  {"zvl256b", "+zvl128b", check_implicit_always},
-  {"zvl128b", "+zvl64b", check_implicit_always},
-  {"zvl64b", "+zvl32b", check_implicit_always},
-
-  {"zcb", "+zca", check_implicit_always},
-  {"zcd", "+d,+zca", check_implicit_always},
-  {"zcf", "+f,+zca", check_implicit_always},
-  {"zcmp", "+zca", check_implicit_always},
-  {"zcmop", "+zca", check_implicit_always},
-
-  {"shcounterenw", "+h", check_implicit_always},
-  {"shgatpa", "+h", check_implicit_always},
-  {"shtvala", "+h", check_implicit_always},
-  {"shvsatpa", "+h", check_implicit_always},
-  {"shvstvala", "+h", check_implicit_always},
-  {"shvstvecd", "+h", check_implicit_always},
-  {"h", "+zicsr", check_implicit_always},
-  {"zhinx", "+zhinxmin", check_implicit_always},
-  {"zhinxmin", "+zfinx", check_implicit_always},
-
-  {"q", "+d", check_implicit_always},
-  {"zqinx", "+zdinx", check_implicit_always},
-
-  {"d", "+f", check_implicit_always},
-  {"zdinx", "+zfinx", check_implicit_always},
-
-  {"zfa", "+f", check_implicit_always},
-  {"zfbfmin", "+zfhmin", check_implicit_always},
-  {"zfh", "+zfhmin", check_implicit_always},
-  {"zfhmin", "+f", check_implicit_always},
-  {"zfinx", "+zicsr", check_implicit_always},
-  {"f", "+zicsr", check_implicit_always},
-
-  {"b", "+zba,+zbb,+zbs", check_implicit_always},
-
-  {"zk", "+zkn,+zkr,+zkt", check_implicit_always},
-  {"zkn", "+zbkb,+zbkc,+zbkx,+zkne,+zknd,+zknh", check_implicit_always},
-  {"zks", "+zbkb,+zbkc,+zbkx,+zksed,+zksh", check_implicit_always},
-  {"zvbb", "+zvkb", check_implicit_always},
-  {"zvkng", "+zvkn,+zvkg", check_implicit_always},
-  {"zvknc", "+zvkn,+zvbc", check_implicit_always},
-  {"zvkn", "+zvkned,+zvknhb,+zvkb,+zvkt", check_implicit_always},
-  {"zvksg", "+zvks,+zvkg", check_implicit_always},
-  {"zvksc", "+zvks,+zvbc", check_implicit_always},
-  {"zvks", "+zvksed,+zvksh,+zvkb,+zvkt", check_implicit_always},
-
-  {"smaia", "+ssaia", check_implicit_always},
-  {"smcsrind", "+sscsrind", check_implicit_always},
-  {"smcntrpmf", "+zicsr", check_implicit_always},
-  {"smstateen", "+ssstateen", check_implicit_always},
-  {"smepmp", "+zicsr", check_implicit_always},
-
-  {"ssaia", "+zicsr", check_implicit_always},
-  {"sscsrind", "+zicsr", check_implicit_always},
-  {"sscofpmf", "+zicsr", check_implicit_always},
-  {"sscounterenw", "+zicsr", check_implicit_always},
-  {"ssstateen", "+zicsr", check_implicit_always},
-  {"sstc", "+zicsr", check_implicit_always},
-  {"sstvala", "+zicsr", check_implicit_always},
-  {"sstvecd", "+zicsr", check_implicit_always},
-  {"ssu64xl", "+zicsr", check_implicit_always},
-
-  {"svade", "+zicsr", check_implicit_always},
-  {"svadu", "+zicsr", check_implicit_always},
-  {"svbare", "+zicsr", check_implicit_always},
-  {NULL, NULL, NULL}
+  {"g", "+i,+m,+a,+f,+d,+zicsr,+zifencei", check_implicit_always, IMPLY},
+  {"e", "+i", check_implicit_always, IMPLY},
+  {"i", "+zicsr,+zifencei", check_implicit_for_i, IMPLY},
+  {"zicntr", "+zicsr", check_implicit_always, IMPLY},
+  {"zihpm", "+zicsr", check_implicit_always, IMPLY},
+
+  {"m", "+zmmul", check_implicit_always, INCLUDE},
+
+  {"zabha", "+zaamo", check_implicit_always, IMPLY},
+  {"zacas", "+zaamo", check_implicit_always, IMPLY},
+  {"a", "+zaamo,+zalrsc", check_implicit_always, EQUAL},
+
+  {"xsfvcp", "+zve32x", check_implicit_always, IMPLY},
+  {"v", "+zve64d,+zvl128b", check_implicit_always, IMPLY},
+  {"zvfh", "+zvfhmin,+zfhmin", check_implicit_always, IMPLY},
+  {"zvfhmin", "+zve32f", check_implicit_always, IMPLY},
+  {"zvfbfwma", "+zve32f,+zfbfmin", check_implicit_always, IMPLY},
+  {"zvfbfmin", "+zve32f", check_implicit_always, IMPLY},
+  {"zve64d", "+d,+zve64f", check_implicit_always, IMPLY},
+  {"zve64f", "+zve32f,+zve64x,+zvl64b", check_implicit_always, IMPLY},
+  {"zve32f", "+f,+zve32x,+zvl32b", check_implicit_always, IMPLY},
+  {"zve64x", "+zve32x,+zvl64b", check_implicit_always, IMPLY},
+  {"zve32x", "+zvl32b,+zicsr", check_implicit_always, IMPLY},
+  {"zvl65536b", "+zvl32768b", check_implicit_always, IMPLY},
+  {"zvl32768b", "+zvl16384b", check_implicit_always, IMPLY},
+  {"zvl16384b", "+zvl8192b", check_implicit_always, IMPLY},
+  {"zvl8192b", "+zvl4096b", check_implicit_always, IMPLY},
+  {"zvl4096b", "+zvl2048b", check_implicit_always, IMPLY},
+  {"zvl2048b", "+zvl1024b", check_implicit_always, IMPLY},
+  {"zvl1024b", "+zvl512b", check_implicit_always, IMPLY},
+  {"zvl512b", "+zvl256b", check_implicit_always, IMPLY},
+  {"zvl256b", "+zvl128b", check_implicit_always, IMPLY},
+  {"zvl128b", "+zvl64b", check_implicit_always, IMPLY},
+  {"zvl64b", "+zvl32b", check_implicit_always, IMPLY},
+
+  {"zcb", "+zca", check_implicit_always, IMPLY},
+  {"zcd", "+d,+zca", check_implicit_always, IMPLY},
+  {"zcf", "+f,+zca", check_implicit_always, IMPLY},
+  {"zcmp", "+zca", check_implicit_always, IMPLY},
+  {"zcmop", "+zca", check_implicit_always, IMPLY},
+
+  {"shcounterenw", "+h", check_implicit_always, IMPLY},
+  {"shgatpa", "+h", check_implicit_always, IMPLY},
+  {"shtvala", "+h", check_implicit_always, IMPLY},
+  {"shvsatpa", "+h", check_implicit_always, IMPLY},
+  {"shvstvala", "+h", check_implicit_always, IMPLY},
+  {"shvstvecd", "+h", check_implicit_always, IMPLY},
+  {"h", "+zicsr", check_implicit_always, IMPLY},
+  {"zhinx", "+zhinxmin", check_implicit_always, INCLUDE},
+  {"zhinxmin", "+zfinx", check_implicit_always, IMPLY},
+
+  {"q", "+d", check_implicit_always, IMPLY},
+  {"zqinx", "+zdinx", check_implicit_always, IMPLY},
+
+  {"d", "+f", check_implicit_always, IMPLY},
+  {"zdinx", "+zfinx", check_implicit_always, IMPLY},
+
+  {"zfa", "+f", check_implicit_always, IMPLY},
+  {"zfbfmin", "+zfhmin", check_implicit_always, IMPLY},
+  {"zfh", "+zfhmin", check_implicit_always, INCLUDE},
+  {"zfhmin", "+f", check_implicit_always, IMPLY},
+  {"zfinx", "+zicsr", check_implicit_always, IMPLY},
+  {"f", "+zicsr", check_implicit_always, IMPLY},
+
+  /* c = zca + zcf (if f) + zcd (if d).  */
+  {"c", "+zcf", check_implicit_for_c_zcf, EQUAL},
+  {"c", "+zcd", check_implicit_for_c_zcd, EQUAL},
+  {"c", "+zca", check_implicit_always, EQUAL},
+
+  {"b", "+zba,+zbb,+zbs", check_implicit_always, EQUAL},
+
+  {"zk", "+zkn,+zkr,+zkt", check_implicit_always, EQUAL},
+  {"zkn", "+zbkb,+zbkc,+zbkx,+zkne,+zknd,+zknh", check_implicit_always, EQUAL},
+  {"zks", "+zbkb,+zbkc,+zbkx,+zksed,+zksh", check_implicit_always, EQUAL},
+  {"zvbb", "+zvkb", check_implicit_always, INCLUDE},
+  {"zvkng", "+zvkn,+zvkg", check_implicit_always, EQUAL},
+  {"zvknc", "+zvkn,+zvbc", check_implicit_always, EQUAL},
+  {"zvkn", "+zvkned,+zvknhb,+zvkb,+zvkt", check_implicit_always, EQUAL},
+  {"zvksg", "+zvks,+zvkg", check_implicit_always, EQUAL},
+  {"zvksc", "+zvks,+zvbc", check_implicit_always, EQUAL},
+  {"zvks", "+zvksed,+zvksh,+zvkb,+zvkt", check_implicit_always, EQUAL},
+
+  {"smaia", "+ssaia", check_implicit_always, IMPLY},
+  {"smcsrind", "+sscsrind", check_implicit_always, IMPLY},
+  {"smcntrpmf", "+zicsr", check_implicit_always, IMPLY},
+  {"smstateen", "+ssstateen", check_implicit_always, IMPLY},
+  {"smepmp", "+zicsr", check_implicit_always, IMPLY},
+
+  {"ssaia", "+zicsr", check_implicit_always, IMPLY},
+  {"sscsrind", "+zicsr", check_implicit_always, IMPLY},
+  {"sscofpmf", "+zicsr", check_implicit_always, IMPLY},
+  {"sscounterenw", "+zicsr", check_implicit_always, IMPLY},
+  {"ssstateen", "+zicsr", check_implicit_always, IMPLY},
+  {"sstc", "+zicsr", check_implicit_always, IMPLY},
+  {"sstvala", "+zicsr", check_implicit_always, IMPLY},
+  {"sstvecd", "+zicsr", check_implicit_always, IMPLY},
+  {"ssu64xl", "+zicsr", check_implicit_always, IMPLY},
+
+  {"svade", "+zicsr", check_implicit_always, IMPLY},
+  {"svadu", "+zicsr", check_implicit_always, IMPLY},
+  {"svbare", "+zicsr", check_implicit_always, IMPLY},
+  {NULL, NULL, NULL, 0}
 };
 
 /* For default_enable field, decide if the extension should
@@ -1895,6 +1926,79 @@  riscv_parsing_subset_version (const char *p,
   return p;
 }
 
+/* Extract extension and it's versions from the string ARCH.  */
+
+static bool
+riscv_parse_extract_subset (riscv_parse_subset_t *rps, const char *arch, char s,
+			    char *subset, const char **end_of_version,
+			    int *major_version, int *minor_version)
+{
+  char *q = subset;
+  /* Extract the whole prefixed extension by S.  */
+  while (*q != '\0' && *q != s)
+    q++;
+
+  /* Look forward to the first letter which is not <major>p<minor>.  */
+  bool find_any_version = false;
+  bool find_minor_version = false;
+  size_t len = q - subset;
+  size_t i;
+  for (i = len; i > 0; i--)
+    {
+      q--;
+      if (ISDIGIT (*q))
+	  find_any_version = true;
+      else if (find_any_version
+	       && !find_minor_version
+	       && *q == 'p'
+	       && ISDIGIT (*(q - 1)))
+	find_minor_version = true;
+      else
+	break;
+    }
+  if (len > 0)
+    q++;
+
+  /* Check if the end of extension is 'p' or not.  If yes, then
+     the second letter from the end cannot be number.  */
+  if (len > 1 && *(q - 1) == 'p' && ISDIGIT (*(q - 2)))
+    {
+      *q = '\0';
+      rps->error_handler
+	(_("%s: invalid prefixed ISA extension `%s' ends with <number>p"),
+	   arch, subset);
+      return false;
+    }
+
+  *end_of_version =
+    riscv_parsing_subset_version (q, major_version, minor_version);
+  *q = '\0';
+  if (*end_of_version == NULL)
+    return false;
+
+  const char *msg = NULL;
+  bool standard = strlen (subset) == 1
+		  && riscv_get_prefix_class (subset) == RV_ISA_CLASS_SINGLE;
+  if (strlen (subset) == 0)
+    msg = "";
+  else if (standard
+	   && riscv_ext_order[(*subset - 'a')] == 0)
+    msg = _("standard ");
+  else if (!standard
+	   && rps->check_unknown_prefixed_ext
+	   && !riscv_recognized_prefixed_ext (subset))
+    msg = _("prefixed ");
+  if (msg != NULL)
+    {
+      rps->error_handler
+	(_("%s: unknown %sISA extension `%s'"),
+	   arch, msg, subset);
+      return false;
+    }
+
+  return true;
+}
+
 /* Parsing function for both standard and prefixed extensions.
 
    Return Value:
@@ -1930,6 +2034,8 @@  riscv_parse_extensions (riscv_parse_subset_t *rps,
       char *subset = xstrdup (p);
       char *q = subset;	/* Start of version.  */
       const char *end_of_version;
+      int major_version = RISCV_UNKNOWN_VERSION;
+      int minor_version = RISCV_UNKNOWN_VERSION;
       bool implicit = false;
 
       enum riscv_prefix_ext_class class = riscv_get_prefix_class (p);
@@ -1944,62 +2050,19 @@  riscv_parse_extensions (riscv_parse_subset_t *rps,
 	      return NULL;
 	    }
 	  q++;
-	}
-      else
-	{
-	  /* Extract the whole prefixed extension by '_'.  */
-	  while (*++q != '\0' && *q != '_')
-	    ;
-	  /* Look forward to the first letter which is not <major>p<minor>.  */
-	  bool find_any_version = false;
-	  bool find_minor_version = false;
-	  while (1)
-	    {
-	      q--;
-	      if (ISDIGIT (*q))
-		find_any_version = true;
-	      else if (find_any_version
-		       && !find_minor_version
-		       && *q == 'p'
-		       && ISDIGIT (*(q - 1)))
-	      find_minor_version = true;
-	      else
-		break;
-	    }
-	  q++;
-
-	  /* Check if the end of extension is 'p' or not.  If yes, then
-	     the second letter from the end cannot be number.  */
-	  if (*(q - 1) == 'p' && ISDIGIT (*(q - 2)))
+	  end_of_version =
+	    riscv_parsing_subset_version (q, &major_version, &minor_version);
+	  *q = '\0';
+	  if (end_of_version == NULL)
 	    {
-	      *q = '\0';
-	      rps->error_handler
-		(_("%s: invalid prefixed ISA extension `%s' ends with <number>p"),
-		 arch, subset);
 	      free (subset);
 	      return NULL;
 	    }
 	}
-
-      int major_version = RISCV_UNKNOWN_VERSION;
-      int minor_version = RISCV_UNKNOWN_VERSION;
-      end_of_version =
-	riscv_parsing_subset_version (q, &major_version, &minor_version);
-      *q = '\0';
-      if (end_of_version == NULL)
-	{
-	  free (subset);
-	  return NULL;
-	}
-
-      /* Check if the prefixed extension name is well-formed.  */
-      if (class != RV_ISA_CLASS_SINGLE
-	  && rps->check_unknown_prefixed_ext
-	  && !riscv_recognized_prefixed_ext (subset))
+      else if (!riscv_parse_extract_subset (rps, arch, '_', subset,
+					    &end_of_version, &major_version,
+					    &minor_version))
 	{
-	  rps->error_handler
-	    (_("%s: unknown prefixed ISA extension `%s'"),
-	     arch, subset);
 	  free (subset);
 	  return NULL;
 	}
@@ -2032,7 +2095,7 @@  riscv_parse_extensions (riscv_parse_subset_t *rps,
 }
 
 static bool
-riscv_update_subset1 (riscv_parse_subset_t *, riscv_subset_t *, const char *);
+riscv_update_subset1 (riscv_parse_subset_t *, const char *);
 
 /* Add the implicit extensions.  */
 
@@ -2044,8 +2107,8 @@  riscv_parse_add_implicit_subsets (riscv_parse_subset_t *rps)
     {
       riscv_subset_t *subset = NULL;
       if (riscv_lookup_subset (rps->subset_list, t->ext, &subset)
-	&& t->check_func (subset))
-      riscv_update_subset1 (rps, subset, t->implicit_exts);
+	  && t->check_func (rps, subset))
+	riscv_update_subset1 (rps, t->implicit_exts);
     }
 }
 
@@ -2376,119 +2439,123 @@  riscv_remove_subset (riscv_subset_list_t *subset_list,
     }
 }
 
-/* Auxiliary to add/remove extensions to/from the subset list.
-   This is called from riscv_update_subset or riscv_parse_add_implicit_subsets.
+/* If the removed subsets are belonged to supersets, then also remove them.  */
+
+static void
+riscv_update_superset (riscv_parse_subset_t *rps)
+{
+  struct riscv_implicit_subset *t = riscv_implicit_subsets;
+  int n = sizeof (riscv_implicit_subsets) / sizeof (*t);
+  for (n = n - 2; n > 0; n--)
+    {
+      if (t[n].type != EQUAL
+	  || !t[n].check_func (rps, NULL))
+	continue;
 
-   The EXPLICIT_SUBSET, the corresponding explicit extension.  It is NULL means
-   called from riscv_update_subset./
+      const char *p = t[n].implicit_exts;
+      bool superset = true;
+      do
+	{
+	  /* Skip `+' and `-'.  */
+	  p++;
+	  char *ext = xstrdup (p);
+	  const char *end_of_version;
+	  int major_version = RISCV_UNKNOWN_VERSION;
+	  int minor_version = RISCV_UNKNOWN_VERSION;
+	  riscv_parse_extract_subset (rps, t[n].implicit_exts, ',', ext,
+				      &end_of_version, &major_version,
+				      &minor_version);
+	  riscv_subset_t *subset = NULL;
+	  if (!riscv_lookup_subset (rps->subset_list, ext, &subset))
+	    {
+	      superset = false;
+	      free (ext);
+	      break;
+	    }
+	  p += end_of_version - ext;
+	  free (ext);
+	}
+      while (*p++ == ',');
+
+      if (!superset)
+	riscv_remove_subset (rps->subset_list, t[n].ext);
+    }
+}
 
-   The IMPLICIT_EXTS, +extension[version] [,...,+extension_n[version_n]]
-		      -extension [,...,-extension_n],
-		      full ISA.  */
+/* Auxiliary to add/remove extensions to/from the subset list.
+
+   The EXTS, +extension[version] [,...,+extension_n[version_n]]
+	     -extension [,...,-extension_n],
+	     full ISA.  */
 
 static bool
 riscv_update_subset1 (riscv_parse_subset_t *rps,
-		      riscv_subset_t *explicit_subset,
-		      const char *implicit_exts)
+		      const char *exts)
 {
-  const char *p = implicit_exts;
-  const char *errmsg_internal = explicit_subset == NULL ? "" : _("internal: ");
-  const char *errmsg_caller = explicit_subset == NULL
-			      ? ".option arch" : "riscv_implicit_subsets";
+  const char *p = exts;
+  enum btype {UNCLEAR, ADD, SUB};
+  enum btype behavior = UNCLEAR;
 
   do
     {
-      int major_version = RISCV_UNKNOWN_VERSION;
-      int minor_version = RISCV_UNKNOWN_VERSION;
-
-      bool removed = false;
       switch (*p)
 	{
-	case '+': removed = false; break;
-	case '-': removed = true; break;
+	case '+': behavior = (behavior == SUB) ? UNCLEAR : ADD; break;
+	case '-': behavior = (behavior == ADD) ? UNCLEAR : SUB; break;
 	default:
 	  riscv_release_subset_list (rps->subset_list);
 	  return riscv_parse_subset (rps, p);
 	}
       ++p;
 
-      char *subset = xstrdup (p);
-      char *q = subset;
-      const char *end_of_version;
-      /* Extract the whole prefixed extension by ','.  */
-      while (*q != '\0' && *q != ',')
-        q++;
-
-      /* Look forward to the first letter which is not <major>p<minor>.  */
-      bool find_any_version = false;
-      bool find_minor_version = false;
-      size_t len = q - subset;
-      size_t i;
-      for (i = len; i > 0; i--)
-        {
-	  q--;
-	  if (ISDIGIT (*q))
-	    find_any_version = true;
-	  else if (find_any_version
-		   && !find_minor_version
-		   && *q == 'p'
-		   && ISDIGIT (*(q - 1)))
-	    find_minor_version = true;
-	  else
-	    break;
-	}
-      if (len > 0)
-	q++;
-
-      /* Check if the end of extension is 'p' or not.  If yes, then
-	 the second letter from the end cannot be number.  */
-      if (len > 1 && *(q - 1) == 'p' && ISDIGIT (*(q - 2)))
+      if (behavior == UNCLEAR)
 	{
-	  *q = '\0';
 	  rps->error_handler
-	    (_("%sinvalid ISA extension ends with <number>p in %s `%s'"),
-	       errmsg_internal, errmsg_caller, implicit_exts);
-	  free (subset);
+	    (_("%s: cannot mix `+' and `-' at the same time"), exts);
 	  return false;
 	}
 
-      end_of_version =
-	riscv_parsing_subset_version (q, &major_version, &minor_version);
-      *q = '\0';
-      if (end_of_version == NULL)
+      char *subset = xstrdup (p);
+      const char *end_of_version;
+      int major_version = RISCV_UNKNOWN_VERSION;
+      int minor_version = RISCV_UNKNOWN_VERSION;
+      if (!riscv_parse_extract_subset (rps, exts, ',', subset, &end_of_version,
+				       &major_version, &minor_version))
 	{
 	  free (subset);
 	  return false;
 	}
 
-      if (strlen (subset) == 0
-	  || (strlen (subset) == 1
-	      && riscv_ext_order[(*subset - 'a')] == 0)
-	  || (strlen (subset) > 1
-	      && rps->check_unknown_prefixed_ext
-	      && !riscv_recognized_prefixed_ext (subset)))
+      if (behavior == SUB)
 	{
-	  rps->error_handler
-	    (_("%sunknown ISA extension `%s' in %s `%s'"),
-	     errmsg_internal, subset, errmsg_caller, implicit_exts);
-	  free (subset);
-	  return false;
-	}
-
-      if (explicit_subset == NULL
-	  && (strcmp (subset, "i") == 0
+	  if (strcmp (subset, "i") == 0
 	      || strcmp (subset, "e") == 0
-	      || strcmp (subset, "g") == 0))
-	{
-	  rps->error_handler
-	    (_("%scannot + or - base extension `%s' in %s `%s'"),
-	       errmsg_internal, subset, errmsg_caller, implicit_exts);
-	  free (subset);
-	  return false;
-	}
+	      || strcmp (subset, "g") == 0)
+	    {
+	      rps->error_handler
+		(_("%s: cannot remove base extension `%s'"), exts, subset);
+	      free (subset);
+	      return false;
+	    }
 
-      if (removed)
-	riscv_remove_subset (rps->subset_list, subset);
+	  struct riscv_implicit_subset *t = riscv_implicit_subsets;
+	  for (; t->ext; t++)
+	    {
+	      if ((t->type == INCLUDE || t->type == EQUAL)
+		  && strcmp (subset, t->ext) == 0
+		  && t->check_func (rps, NULL))
+		{
+		  char *include_exts = xstrdup (t->implicit_exts);
+		  char *pp = include_exts;
+		  for (; *pp != '\0'; pp++)
+		    if (*pp == '+')
+		      *pp = '-';
+		  riscv_update_subset1 (rps, include_exts);
+		  free (include_exts);
+		}
+	    }
+	  riscv_remove_subset (rps->subset_list, subset);
+	}
       else
 	{
 	  riscv_subset_t *isubset = NULL;
@@ -2501,13 +2568,10 @@  riscv_update_subset1 (riscv_parse_subset_t *rps,
     }
   while (*p++ == ',');
 
-  bool conflict = false;
-  if (explicit_subset == NULL)
-    {
-      riscv_parse_add_implicit_subsets (rps);
-      conflict = riscv_parse_check_conflicts (rps);
-    }
-  return conflict;
+  if (behavior == SUB)
+    riscv_update_superset (rps);
+
+  return true;
 }
 
 /* Add/Remove an extension to/from the subset list.  This is used for
@@ -2517,7 +2581,9 @@  bool
 riscv_update_subset (riscv_parse_subset_t *rps,
 		     const char *str)
 {
-  return riscv_update_subset1 (rps, NULL, str);
+  riscv_update_subset1 (rps, str);
+  riscv_parse_add_implicit_subsets (rps);
+  return riscv_parse_check_conflicts (rps);
 }
 
 /* Check if the FEATURE subset is supported or not in the subset list.
@@ -2556,10 +2622,9 @@  riscv_multi_subset_supports (riscv_parse_subset_t *rps,
       return riscv_subset_supports (rps, "zifencei");
     case INSN_CLASS_ZIHINTNTL:
       return riscv_subset_supports (rps, "zihintntl");
-    case INSN_CLASS_ZIHINTNTL_AND_C:
+    case INSN_CLASS_ZIHINTNTL_AND_ZCA:
       return (riscv_subset_supports (rps, "zihintntl")
-	      && (riscv_subset_supports (rps, "c")
-		  || riscv_subset_supports (rps, "zca")));
+	      && riscv_subset_supports (rps, "zca"));
     case INSN_CLASS_ZIHINTPAUSE:
       return riscv_subset_supports (rps, "zihintpause");
     case INSN_CLASS_ZIMOP:
@@ -2587,17 +2652,12 @@  riscv_multi_subset_supports (riscv_parse_subset_t *rps,
       return riscv_subset_supports (rps, "d");
     case INSN_CLASS_Q:
       return riscv_subset_supports (rps, "q");
-    case INSN_CLASS_C:
-      return (riscv_subset_supports (rps, "c")
-	      || riscv_subset_supports (rps, "zca"));
-    case INSN_CLASS_F_AND_C:
-      return (riscv_subset_supports (rps, "f")
-	      && (riscv_subset_supports (rps, "c")
-		  || riscv_subset_supports (rps, "zcf")));
-    case INSN_CLASS_D_AND_C:
-      return (riscv_subset_supports (rps, "d")
-	      && (riscv_subset_supports (rps, "c")
-		  || riscv_subset_supports (rps, "zcd")));
+    case INSN_CLASS_ZCA:
+      return riscv_subset_supports (rps, "zca");
+    case INSN_CLASS_ZCF:
+      return riscv_subset_supports (rps, "zcf");
+    case INSN_CLASS_ZCD:
+      return riscv_subset_supports (rps, "zcd");
     case INSN_CLASS_F_INX:
       return (riscv_subset_supports (rps, "f")
 	      || riscv_subset_supports (rps, "zfinx"));
@@ -2804,17 +2864,8 @@  riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
       return "zifencei";
     case INSN_CLASS_ZIHINTNTL:
       return "zihintntl";
-    case INSN_CLASS_ZIHINTNTL_AND_C:
-      if (!riscv_subset_supports (rps, "zihintntl"))
-	{
-	  if (!riscv_subset_supports (rps, "c")
-	      && !riscv_subset_supports (rps, "zca"))
-	    return _("zihintntl' and `c', or `zihintntl' and `zca");
-	  else
-	    return "zihintntl";
-	}
-      else
-	return _("c' or `zca");
+    case INSN_CLASS_ZIHINTNTL_AND_ZCA:
+      return _("zihintntl' and `zca");
     case INSN_CLASS_ZIHINTPAUSE:
       return "zihintpause";
     case INSN_CLASS_ZIMOP:
@@ -2822,7 +2873,7 @@  riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
     case INSN_CLASS_M:
       return "m";
     case INSN_CLASS_ZMMUL:
-      return _ ("m' or `zmmul");
+      return "zmmul";
     case INSN_CLASS_ZAAMO:
       return "zaamo";
     case INSN_CLASS_ZABHA:
@@ -2839,30 +2890,12 @@  riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
       return "d";
     case INSN_CLASS_Q:
       return "q";
-    case INSN_CLASS_C:
-      return _("c' or `zca");
-    case INSN_CLASS_F_AND_C:
-      if (!riscv_subset_supports (rps, "f"))
-	{
-	  if (!riscv_subset_supports (rps, "c")
-	      && !riscv_subset_supports (rps, "zcf"))
-	    return _("f' and `c', or `f' and `zcf"); 
-	  else
-	    return "f";
-	}
-      else
-	return _("c' or `zcf");
-    case INSN_CLASS_D_AND_C:
-      if (!riscv_subset_supports (rps, "d"))
-	{
-	  if (!riscv_subset_supports (rps, "c")
-	      && !riscv_subset_supports (rps, "zcd"))
-	    return _("d' and `c', or `d' and `zcd");
-	  else
-	    return "d";
-	}
-      else
-	return _("c' or `zcd");
+    case INSN_CLASS_ZCA:
+      return "zca";
+    case INSN_CLASS_ZCF:
+       return "zcf";
+    case INSN_CLASS_ZCD:
+      return "zcd";
     case INSN_CLASS_F_INX:
       return _("f' or `zfinx");
     case INSN_CLASS_D_INX:
@@ -2997,7 +3030,7 @@  riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
     case INSN_CLASS_ZCB_AND_ZBB:
       return _("zcb' and `zbb");
     case INSN_CLASS_ZCB_AND_ZMMUL:
-      return _("zcb' and `zmmul', or `zcb' and `m");
+      return _("zcb' and `zmmul");
     case INSN_CLASS_ZCMOP:
       return "zcmop";
     case INSN_CLASS_ZCMP:
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index e19142dca19..14dfd41dcfc 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -367,8 +367,7 @@  riscv_set_arch (const char *s)
   riscv_set_arch_str (&file_arch_str);
   riscv_set_arch_str (&riscv_rps_as.subset_list->arch_str);
 
-  riscv_set_rvc (riscv_subset_supports (&riscv_rps_as, "c")
-		 || riscv_subset_supports (&riscv_rps_as, "zca"));
+  riscv_set_rvc (riscv_subset_supports (&riscv_rps_as, "zca"));
 
   if (riscv_subset_supports (&riscv_rps_as, "ztso"))
     riscv_set_tso ();
@@ -4934,8 +4933,7 @@  s_riscv_option (int x ATTRIBUTE_UNUSED)
       riscv_update_subset (&riscv_rps_as, name);
       riscv_set_arch_str (&riscv_rps_as.subset_list->arch_str);
 
-      riscv_set_rvc (riscv_subset_supports (&riscv_rps_as, "c")
-		     || riscv_subset_supports (&riscv_rps_as, "zca"));
+      riscv_set_rvc (riscv_subset_supports (&riscv_rps_as, "zca"));
 
       if (riscv_subset_supports (&riscv_rps_as, "ztso"))
 	riscv_set_tso ();
diff --git a/gas/testsuite/gas/riscv/attribute-10.d b/gas/testsuite/gas/riscv/attribute-10.d
index 04c322ab1dd..cb3ae5a897f 100644
--- a/gas/testsuite/gas/riscv/attribute-10.d
+++ b/gas/testsuite/gas/riscv/attribute-10.d
@@ -3,4 +3,4 @@ 
 #source: empty.s
 Attribute Section: riscv
 File Attributes
-  Tag_RISCV_arch: "rv32i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0"
+  Tag_RISCV_arch: "rv32i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0_zcf1p0"
diff --git a/gas/testsuite/gas/riscv/imply.d b/gas/testsuite/gas/riscv/imply.d
index 26eff8c650a..f974eac188a 100644
--- a/gas/testsuite/gas/riscv/imply.d
+++ b/gas/testsuite/gas/riscv/imply.d
@@ -64,6 +64,11 @@  SYMBOL TABLE:
 [0-9a-f]+ l       .text	0+000 \$xrv32i2p1_f2p2_zicsr2p0_zfhmin1p0
 [0-9a-f]+ l       .text	0+000 \$xrv32i2p1_zicsr2p0_zfinx1p0
 [0-9a-f]+ l       .text	0+000 \$xrv32i2p1_f2p2_zicsr2p0
+[0-9a-f]+ l       .text	0+000 \$xrv32i2p1_c2p0_zca1p0
+[0-9a-f]+ l       .text	0+000 \$xrv32i2p1_f2p2_c2p0_zicsr2p0_zca1p0_zcf1p0
+[0-9a-f]+ l       .text	0+000 \$xrv64i2p1_f2p2_c2p0_zicsr2p0_zca1p0
+[0-9a-f]+ l       .text	0+000 \$xrv32i2p1_f2p2_d2p2_c2p0_zicsr2p0_zca1p0_zcd1p0_zcf1p0
+[0-9a-f]+ l       .text	0+000 \$xrv64i2p1_f2p2_d2p2_c2p0_zicsr2p0_zca1p0_zcd1p0
 [0-9a-f]+ l       .text	0+000 \$xrv32i2p1_b1p0_zba1p0_zbb1p0_zbs1p0
 [0-9a-f]+ l       .text	0+000 \$xrv32i2p1_zbkb1p0_zbkc1p0_zbkx1p0_zk1p0_zkn1p0_zknd1p0_zkne1p0_zknh1p0_zkr1p0_zkt1p0
 [0-9a-f]+ l       .text	0+000 \$xrv32i2p1_zbkb1p0_zbkc1p0_zbkx1p0_zkn1p0_zknd1p0_zkne1p0_zknh1p0
diff --git a/gas/testsuite/gas/riscv/imply.s b/gas/testsuite/gas/riscv/imply.s
index dabb08d8c8b..31664b9b6ac 100644
--- a/gas/testsuite/gas/riscv/imply.s
+++ b/gas/testsuite/gas/riscv/imply.s
@@ -1,15 +1,15 @@ 
-.macro imply string base=i
+.macro imply string xlen=32 base=i
 .option push
-.option arch, rv32\base\string
+.option arch, rv\xlen\base\string
 nop
 .option pop
 .endm
 
 .text
-imply ,g
-imply ,e
-imply ,i
-imply ,i2p0
+imply ,,g
+imply ,,e
+imply ,,i
+imply ,,i2p0
 
 imply zicntr
 imply zihpm
@@ -72,6 +72,12 @@  imply zfhmin
 imply zfinx
 imply f
 
+imply c
+imply f_c,32
+imply f_c,64
+imply d_c,32
+imply d_c,64
+
 imply b
 
 imply zk
diff --git a/gas/testsuite/gas/riscv/m-ext-fail-noarch-64.l b/gas/testsuite/gas/riscv/m-ext-fail-noarch-64.l
index db9c8fb8396..b646c3b16ef 100644
--- a/gas/testsuite/gas/riscv/m-ext-fail-noarch-64.l
+++ b/gas/testsuite/gas/riscv/m-ext-fail-noarch-64.l
@@ -1,13 +1,13 @@ 
 .*Assembler messages:
-.*: Error: unrecognized opcode `mul a0,a1,a2', extension `m' or `zmmul' required
-.*: Error: unrecognized opcode `mulh a0,a1,a2', extension `m' or `zmmul' required
-.*: Error: unrecognized opcode `mulhsu a0,a1,a2', extension `m' or `zmmul' required
-.*: Error: unrecognized opcode `mulhu a0,a1,a2', extension `m' or `zmmul' required
+.*: Error: unrecognized opcode `mul a0,a1,a2', extension `zmmul' required
+.*: Error: unrecognized opcode `mulh a0,a1,a2', extension `zmmul' required
+.*: Error: unrecognized opcode `mulhsu a0,a1,a2', extension `zmmul' required
+.*: Error: unrecognized opcode `mulhu a0,a1,a2', extension `zmmul' required
 .*: Error: unrecognized opcode `div a0,a1,a2', extension `m' required
 .*: Error: unrecognized opcode `divu a0,a1,a2', extension `m' required
 .*: Error: unrecognized opcode `rem a0,a1,a2', extension `m' required
 .*: Error: unrecognized opcode `remu a0,a1,a2', extension `m' required
-.*: Error: unrecognized opcode `mulw a0,a1,a2', extension `m' or `zmmul' required
+.*: Error: unrecognized opcode `mulw a0,a1,a2', extension `zmmul' required
 .*: Error: unrecognized opcode `divw a0,a1,a2', extension `m' required
 .*: Error: unrecognized opcode `divuw a0,a1,a2', extension `m' required
 .*: Error: unrecognized opcode `remw a0,a1,a2', extension `m' required
diff --git a/gas/testsuite/gas/riscv/mapping-symbols.d b/gas/testsuite/gas/riscv/mapping-symbols.d
index 057454bf991..f7d5b6f4d0c 100644
--- a/gas/testsuite/gas/riscv/mapping-symbols.d
+++ b/gas/testsuite/gas/riscv/mapping-symbols.d
@@ -9,44 +9,44 @@  SYMBOL TABLE:
 0+00 l    d  .data	0+00 .data
 0+00 l    d  .bss	0+00 .bss
 0+00 l    d  .text.cross.section.A	0+00 .text.cross.section.A
-0+00 l       .text.cross.section.A	0+00 \$xrv32i2p1_c2p0
+0+00 l       .text.cross.section.A	0+00 \$xrv32i2p1_c2p0_zca1p0
 0+00 l    d  .text.cross.section.B	0+00 .text.cross.section.B
-0+00 l       .text.cross.section.B	0+00 \$xrv32i2p1_c2p0
+0+00 l       .text.cross.section.B	0+00 \$xrv32i2p1_c2p0_zca1p0
 0+02 l       .text.cross.section.B	0+00 \$xrv32i2p1
 0+00 l    d  .text.data	0+00 .text.data
 0+00 l       .text.data	0+00 \$d
-0+08 l       .text.data	0+00 \$xrv32i2p1_c2p0
+0+08 l       .text.data	0+00 \$xrv32i2p1_c2p0_zca1p0
 0+0c l       .text.data	0+00 \$d
 0+00 l    d  .text.odd.align.start.insn	0+00 .text.odd.align.start.insn
-0+00 l       .text.odd.align.start.insn	0+00 \$xrv32i2p1_c2p0
+0+00 l       .text.odd.align.start.insn	0+00 \$xrv32i2p1_c2p0_zca1p0
 0+02 l       .text.odd.align.start.insn	0+00 \$d
 0+08 l       .text.odd.align.start.insn	0+00 \$xrv32i2p1
 0+00 l    d  .text.odd.align.start.data	0+00 .text.odd.align.start.data
 0+00 l       .text.odd.align.start.data	0+00 \$d
 0+00 l    d  .text.zero.fill.first	0+00 .text.zero.fill.first
-0+00 l       .text.zero.fill.first	0+00 \$xrv32i2p1_c2p0
+0+00 l       .text.zero.fill.first	0+00 \$xrv32i2p1_c2p0_zca1p0
 0+00 l    d  .text.zero.fill.last	0+00 .text.zero.fill.last
-0+00 l       .text.zero.fill.last	0+00 \$xrv32i2p1_c2p0
+0+00 l       .text.zero.fill.last	0+00 \$xrv32i2p1_c2p0_zca1p0
 0+02 l       .text.zero.fill.last	0+00 \$x
 0+00 l    d  .text.zero.fill.align.A	0+00 .text.zero.fill.align.A
-0+00 l       .text.zero.fill.align.A	0+00 \$xrv32i2p1_c2p0
+0+00 l       .text.zero.fill.align.A	0+00 \$xrv32i2p1_c2p0_zca1p0
 0+00 l    d  .text.zero.fill.align.B	0+00 .text.zero.fill.align.B
 0+00 l       .text.zero.fill.align.B	0+00 \$xrv32i2p1
 0+00 l    d  .text.last.section	0+00 .text.last.section
 0+00 l       .text.last.section	0+00 \$xrv32i2p1
 0+04 l       .text.last.section	0+00 \$d
 0+00 l    d  .text.section.padding	0+00 .text.section.padding
-0+00 l       .text.section.padding	0+00 \$xrv32i2p1_c2p0
-0+04 l       .text.section.padding	0+00 \$xrv32i2p1_a2p1_c2p0_zaamo1p0_zalrsc1p0
+0+00 l       .text.section.padding	0+00 \$xrv32i2p1_c2p0_zca1p0
+0+04 l       .text.section.padding	0+00 \$xrv32i2p1_a2p1_c2p0_zaamo1p0_zalrsc1p0_zca1p0
 0+06 l       .text.section.padding	0+00 \$d
 0+00 l    d  .text.relax.align	0+00 .text.relax.align
-0+00 l       .text.relax.align	0+00 \$xrv32i2p1_c2p0
+0+00 l       .text.relax.align	0+00 \$xrv32i2p1_c2p0_zca1p0
 0+08 l       .text.relax.align	0+00 \$xrv32i2p1
 0+0a l       .text.section.padding	0+00 \$x
 0+03 l       .text.odd.align.start.insn	0+00 \$d
 0+04 l       .text.odd.align.start.insn	0+00 \$x
 0+01 l       .text.odd.align.start.data	0+00 \$d
-0+02 l       .text.odd.align.start.data	0+00 \$xrv32i2p1_c2p0
+0+02 l       .text.odd.align.start.data	0+00 \$xrv32i2p1_c2p0_zca1p0
 0+00 l    d  .riscv.attributes	0+00 .riscv.attributes
 0+00 g       .text.cross.section.A	0+00 funcA
 0+00 g       .text.cross.section.B	0+00 funcB
diff --git a/gas/testsuite/gas/riscv/march-ok-reorder.d b/gas/testsuite/gas/riscv/march-ok-reorder.d
index 712c1bdff4d..883d6a2e1cd 100644
--- a/gas/testsuite/gas/riscv/march-ok-reorder.d
+++ b/gas/testsuite/gas/riscv/march-ok-reorder.d
@@ -4,4 +4,4 @@ 
 
 Attribute Section: riscv
 File Attributes
-  Tag_RISCV_arch: "rv32i2p0_m1p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zba1p0_xbar2p0_xfoo2p0"
+  Tag_RISCV_arch: "rv32i2p0_m1p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0_zcf1p0_zba1p0_xbar2p0_xfoo2p0"
diff --git a/gas/testsuite/gas/riscv/option-arch-attr.d b/gas/testsuite/gas/riscv/option-arch-attr.d
index 8c1f6658796..27df69dc176 100644
--- a/gas/testsuite/gas/riscv/option-arch-attr.d
+++ b/gas/testsuite/gas/riscv/option-arch-attr.d
@@ -4,4 +4,4 @@ 
 
 Attribute Section: riscv
 File Attributes
-  Tag_RISCV_arch: "rv64i2p0_c2p0"
+  Tag_RISCV_arch: "rv64i2p0_c2p0_zca1p0"
diff --git a/gas/testsuite/gas/riscv/option-arch-dis.d b/gas/testsuite/gas/riscv/option-arch-dis.d
index 6768fe72806..980312fcdb1 100644
--- a/gas/testsuite/gas/riscv/option-arch-dis.d
+++ b/gas/testsuite/gas/riscv/option-arch-dis.d
@@ -8,10 +8,10 @@  SYMBOL TABLE:
 0+00 l    d  .text	0+00 .text
 0+00 l    d  .data	0+00 .data
 0+00 l    d  .bss	0+00 .bss
-0+00 l       .text	0+00 \$xrv64i2p0_c2p0
+0+00 l       .text	0+00 \$xrv64i2p0_c2p0_zca1p0
 0+02 l       .text	0+00 \$xrv64i2p0_f2p0_d2p0_xvendor1p0
-0+0a l       .text	0+00 \$xrv64i2p0_m3p0_f2p0_d3p0_c2p0_zmmul1p0_xvendor32x3p0
-0+0c l       .text	0+00 \$xrv32i2p1_c2p0
+0+0a l       .text	0+00 \$xrv64i2p0_m3p0_f2p0_d3p0_c2p0_zmmul1p0_zca1p0_zcd1p0_xvendor32x3p0
+0+0c l       .text	0+00 \$xrv32i2p1_c2p0_zca1p0
 0+00 l    d  .riscv.attributes	0+00 .riscv.attributes
 
 
diff --git a/gas/testsuite/gas/riscv/option-arch-fail.l b/gas/testsuite/gas/riscv/option-arch-fail.l
index b9979a42618..52625123561 100644
--- a/gas/testsuite/gas/riscv/option-arch-fail.l
+++ b/gas/testsuite/gas/riscv/option-arch-fail.l
@@ -1,10 +1,11 @@ 
 .*Assembler messages:
 .*Error: m2p0: ISA string must begin with rv32 or rv64
-.*Error: cannot \+ or \- base extension `i' in .option arch `\-i'
-.*Error: cannot \+ or \- base extension `e' in .option arch `\+e'
-.*Error: cannot \+ or \- base extension `g' in .option arch `\-g'
-.*Error: unknown ISA extension `zsubset' in .option arch `\+zsubset2p0'
-.*Error: unknown ISA extension `f2p0_d' in .option arch `\+f2p0_d2p0'
-.*Error: unknown ISA extension `' in .option arch `\+'
-.*Error: invalid ISA extension ends with <number>p in .option arch `\+xvendor2p'
+.*Error: \-i: cannot remove base extension `i'
+.*Error: \-e: cannot remove base extension `e'
+.*Error: \-g: cannot remove base extension `g'
+.*Error: \+zsubset2p0: unknown prefixed ISA extension `zsubset'
+.*Error: \+f2p0_d2p0: unknown prefixed ISA extension `f2p0_d'
+.*Error: \+: unknown ISA extension `'
+.*Error: \+xvendor2p: invalid prefixed ISA extension `xvendor2p' ends with <number>p
+.*Error: \+c,\-c: cannot mix `\+' and `\-' at the same time
 .*Error: .option pop with no .option push
diff --git a/gas/testsuite/gas/riscv/option-arch-fail.s b/gas/testsuite/gas/riscv/option-arch-fail.s
index 101587aee16..47b469b712f 100644
--- a/gas/testsuite/gas/riscv/option-arch-fail.s
+++ b/gas/testsuite/gas/riscv/option-arch-fail.s
@@ -2,11 +2,12 @@ 
 .option push
 .option arch, m2p0
 .option arch, -i
-.option arch, +e
+.option arch, -e
 .option arch, -g
 .option arch, +zsubset2p0
 .option arch, +f2p0_d2p0
 .option arch, +
 .option arch, +xvendor2p
+.option arch, +c,-c
 .option pop
 .option pop
diff --git a/gas/testsuite/gas/riscv/option-arch-superset.d b/gas/testsuite/gas/riscv/option-arch-superset.d
new file mode 100644
index 00000000000..b721428b95c
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-superset.d
@@ -0,0 +1,154 @@ 
+#as: -misa-spec=20191213
+#source: option-arch-superset.s
+#objdump: --syms --special-syms
+
+.*:[   ]+file format .*
+
+SYMBOL TABLE:
+[0-9a-f]+ l    d  .text	0+00 .text
+[0-9a-f]+ l    d  .data	0+00 .data
+[0-9a-f]+ l    d  .bss	0+00 .bss
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_m2p0_zmmul1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvbb1p0_zvkb1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zicsr2p0_zfinx1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zicsr2p0_zfinx1p0_zhinx1p0_zhinxmin1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zicsr2p0_zfinx1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_f2p2_zicsr2p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_f2p2_zicsr2p0_zfh1p0_zfhmin1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_f2p2_zicsr2p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zalrsc1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zaamo1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbb1p0_zbs1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zba1p0_zbs1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zba1p0_zbb1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkc1p0_zbkx1p0_zksed1p0_zksh1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkx1p0_zksed1p0_zksh1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zksed1p0_zksh1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zbkx1p0_zksh1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zbkx1p0_zksed1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkc1p0_zbkx1p0_zknd1p0_zkne1p0_zknh1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkx1p0_zknd1p0_zkne1p0_zknh1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zknd1p0_zkne1p0_zknh1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zbkx1p0_zknd1p0_zknh1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zbkx1p0_zkne1p0_zknh1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zbkx1p0_zknd1p0_zkne1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkc1p0_zbkx1p0_zknd1p0_zkne1p0_zknh1p0_zkr1p0_zkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkx1p0_zknd1p0_zkne1p0_zknh1p0_zkr1p0_zkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zknd1p0_zkne1p0_zknh1p0_zkr1p0_zkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zbkx1p0_zknd1p0_zknh1p0_zkr1p0_zkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zbkx1p0_zkne1p0_zknh1p0_zkr1p0_zkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zbkx1p0_zknd1p0_zkne1p0_zkr1p0_zkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zkr1p0_zkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zbkx1p0_zkn1p0_zknd1p0_zkne1p0_zknh1p0_zkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zbkb1p0_zbkc1p0_zbkx1p0_zkn1p0_zknd1p0_zkne1p0_zknh1p0_zkr1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkned1p0_zvknhb1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvkned1p0_zvknhb1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvknhb1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvkned1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvksed1p0_zvksh1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvksed1p0_zvksh1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvksh1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvksed1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkg1p0_zvkned1p0_zvknhb1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvkg1p0_zvkned1p0_zvknhb1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvkg1p0_zvknhb1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvkg1p0_zvkned1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkg1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvkn1p0_zvkned1p0_zvknhb1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvbc1p0_zvkned1p0_zvknhb1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvbc1p0_zvkb1p0_zvkned1p0_zvknhb1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvbc1p0_zvkb1p0_zvknhb1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvbc1p0_zvkb1p0_zvkned1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvbc1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvkn1p0_zvkned1p0_zvknhb1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkg1p0_zvksed1p0_zvksh1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvkg1p0_zvksed1p0_zvksh1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvkg1p0_zvksh1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvkg1p0_zvksed1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkg1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvks1p0_zvksed1p0_zvksh1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvbc1p0_zvksed1p0_zvksh1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvbc1p0_zvkb1p0_zvksed1p0_zvksh1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvbc1p0_zvkb1p0_zvksh1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvbc1p0_zvkb1p0_zvksed1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvbc1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_zvkb1p0_zvks1p0_zvksed1p0_zvksh1p0_zvkt1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_c2p0_zca1p0
+[0-9a-f]+ l       .text	0+00 \$xrv64i2p1
+[0-9a-f]+ l       .text	0+00 \$xrv64i2p1_c2p0_zca1p0
+[0-9a-f]+ l       .text	0+00 \$xrv64i2p1_f2p2_zicsr2p0
+[0-9a-f]+ l       .text	0+00 \$xrv64i2p1_f2p2_c2p0_zicsr2p0_zca1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_f2p2_zicsr2p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_f2p2_zicsr2p0_zca1p0_zcf1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_f2p2_zicsr2p0_zca1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_f2p2_c2p0_zicsr2p0_zca1p0_zcf1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_f2p2_d2p2_zicsr2p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_f2p2_d2p2_zicsr2p0_zca1p0_zcd1p0_zcf1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_f2p2_d2p2_zicsr2p0_zca1p0_zcd1p0
+[0-9a-f]+ l       .text	0+00 \$xrv32i2p1_f2p2_d2p2_zicsr2p0_zca1p0_zcf1p0
+[0-9a-f]+ l       .text	0+00 \$xrv64i2p1_f2p2_d2p2_zicsr2p0
+[0-9a-f]+ l       .text	0+00 \$xrv64i2p1_f2p2_d2p2_zicsr2p0_zca1p0_zcd1p0
+[0-9a-f]+ l       .text	0+00 \$xrv64i2p1_f2p2_d2p2_c2p0_zicsr2p0_zca1p0_zcd1p0
+[0-9a-f]+ l       .text	0+00 \$xrv64i2p1_f2p2_d2p2_zicsr2p0_zca1p0
+[0-9a-f]+ l    d  .riscv.attributes	0+00 .riscv.attributes
diff --git a/gas/testsuite/gas/riscv/option-arch-superset.s b/gas/testsuite/gas/riscv/option-arch-superset.s
new file mode 100644
index 00000000000..2691b11ce20
--- /dev/null
+++ b/gas/testsuite/gas/riscv/option-arch-superset.s
@@ -0,0 +1,180 @@ 
+.macro case superset subset
+.option arch, rv32i_\superset
+.option arch, -\superset
+nop
+.option arch, rv32i_\superset
+.option arch, -\subset
+nop
+.option arch, rv32i\subset
+.option arch, -\superset
+nop
+.endm
+
+.text
+
+# Superset > Subset
+# Remove the Subset is useless
+
+case m, zmmul
+case zvbb, zvkb
+case zhinx, zhinxmin
+case zfh, zfhmin
+
+# Superset = Subset1 + Subset2 + ...
+# Also remove the Superset when removing Subset1, or/and Subset2 ...
+
+# a = zaamo + zalrsc
+case a, zaamo
+case a, zalrsc
+
+# b = zba + zbb + zbs
+case b, zba
+case b, zbb
+case b, zbs
+
+# zks = zbkb + zbkc + zbkx + zksed + zksh
+case zks, zbkb
+case zks, zbkc
+case zks, zbkx
+case zks, zksed
+case zks, zksh
+
+# zkn = zbkb + zbkc + zbkx + zkne + zknd + zknh
+case zkn, zbkb
+case zkn, zbkc
+case zkn, zbkx
+case zkn, zkne
+case zkn, zknd
+case zkn, zknh
+
+# zk  = zkn + zkr + zkt
+case zk, zbkb
+case zk, zbkc
+case zk, zbkx
+case zk, zkne
+case zk, zknd
+case zk, zknh
+case zk, zkn
+case zk, zkr
+case zk, zkt
+
+# zvkn = zvkb + zvkt + zvkned + zvknhb
+case zvkn, zvkb
+case zvkn, zvkt
+case zvkn, zvkned
+case zvkn, zvknhb
+
+# zvks = zvkb + zvkt + zvksed + zvksh
+case zvks, zvkb
+case zvks, zvkt
+case zvks, zvksed
+case zvks, zvksh
+
+# zvkng = zvkn + zvkg
+case zvkng, zvkb
+case zvkng, zvkt
+case zvkng, zvkned
+case zvkng, zvknhb
+case zvkng, zvkn
+case zvkng, zvkg
+
+# zvknc = zvkn + zvbc
+case zvknc, zvkb
+case zvknc, zvkt
+case zvknc, zvkned
+case zvknc, zvknhb
+case zvknc, zvkn
+case zvknc, zvbc
+
+# zvksg = zvks + zvkg
+case zvksg, zvkb
+case zvksg, zvkt
+case zvksg, zvksed
+case zvksg, zvksh
+case zvksg, zvks
+case zvksg, zvkg
+
+# zvksc = zvks + zvbc
+case zvksc, zvkb
+case zvksc, zvkt
+case zvksc, zvksed
+case zvksc, zvksh
+case zvksc, zvks
+case zvksc, zvbc
+
+# c = zca
+# rv32ic_zca
+.option arch, rv32ic
+.option arch, -c
+nop			# rv32i
+.option arch, rv32ic
+.option arch, -zca
+nop			# rv32i
+.option arch, rv32ic
+.option arch, -zcf,-zcd
+nop			# rv32ic_zca, -zcf and zcd are useless since c = zca
+# rv64ic_zca
+.option arch, rv64ic
+.option arch, -c
+nop			# rv64i
+.option arch, rv64ic
+.option arch, -zca
+nop			# rv64i
+.option arch, rv64ic
+.option arch, -zcf,-zcd
+nop			# rv64ic_zca, -zcf and zcd are useless since c = zca
+# rv64ifc_zicsr_zca
+.option arch, rv64ifc
+.option arch, -c
+nop			# rv64if_zicsr
+.option arch, rv64ifc
+.option arch, -zca
+nop			# rv64if_zicsr
+.option arch, rv64ifc
+.option arch, -zcf,-zcd
+nop			# rv64ifc_zicsr_zca, -zcf and zcd are useless since c = zca
+
+# c = zca + zcf (if f and rv32)
+# rv32ifc_zicsr_zca_zcf
+.option arch, rv32ifc
+.option arch, -c
+nop			# rv32if_zicsr
+.option arch, rv32ifc
+.option arch, -zca
+nop			# rv32if_zicsr_zca_zcf, zcf imply zca
+.option arch, rv32ifc
+.option arch, -zcf
+nop			# rv32if_zicsr_zca
+.option arch, rv32ifc
+.option arch, -zcd
+nop			# rv32ifc_zicsr_zca_zcf, -zcd is useless since c = zca + zcf
+
+# c = zca + zcf (if f and rv32) + zcd (if d)
+# rv32ifdc_zicsr_zca_zcd_zcf
+.option arch, rv32ifdc
+.option arch, -c
+nop			# rv32ifd_zicsr
+.option arch, rv32ifdc
+.option arch, -zca
+nop			# rv32ifd_zicsr_zca_zcd_zcf, zcf and zcd imply zca
+.option arch, rv32ifdc
+.option arch, -zcf
+nop			# rv32ifd_zicsr_zca_zcd
+.option arch, rv32ifdc
+.option arch, -zcd
+nop			# rv32ifd_zicsr_zca_zcf
+
+# c = zca + zcd (if d)
+# rv64ifdc_zicsr_zca_zcd
+.option arch, rv64ifdc
+.option arch, -c
+nop			# rv64ifd_zicsr
+.option arch, rv64ifdc
+.option arch, -zca
+nop			# rv64ifd_zicsr_zca_zcd, zcd imply zca
+.option arch, rv64ifdc
+.option arch, -zcf
+nop			# rv64ifdc_zicsr_zca_zcd, -zcf is useless since c = zca + zcd
+.option arch, rv64ifdc
+.option arch, -zcd
+nop			# rv64ifd_zicsr_zca
diff --git a/gas/testsuite/gas/riscv/option-arch.s b/gas/testsuite/gas/riscv/option-arch.s
index 4d2d261684a..53f919149aa 100644
--- a/gas/testsuite/gas/riscv/option-arch.s
+++ b/gas/testsuite/gas/riscv/option-arch.s
@@ -1,7 +1,8 @@ 
 .attribute arch, "rv64ic"	# file-level, rv64ic
 add	a0, a0, a1
 .option push
-.option arch, +d2p0, -c, +xvendor1p0
+.option arch, +d2p0, +xvendor1p0
+.option arch, -c
 add	a0, a0, a1		# func-level, rv64i_d2p0_xvendor1p0
 frcsr	a0
 .option pop
diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
index 7df15174d60..2d9fb541503 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -452,18 +452,18 @@  enum riscv_insn_class
   INSN_CLASS_NONE,
 
   INSN_CLASS_I,
-  INSN_CLASS_C,
+  INSN_CLASS_ZCA,
   INSN_CLASS_M,
   INSN_CLASS_F,
   INSN_CLASS_D,
   INSN_CLASS_Q,
-  INSN_CLASS_F_AND_C,
-  INSN_CLASS_D_AND_C,
+  INSN_CLASS_ZCF,
+  INSN_CLASS_ZCD,
   INSN_CLASS_ZICOND,
   INSN_CLASS_ZICSR,
   INSN_CLASS_ZIFENCEI,
   INSN_CLASS_ZIHINTNTL,
-  INSN_CLASS_ZIHINTNTL_AND_C,
+  INSN_CLASS_ZIHINTNTL_AND_ZCA,
   INSN_CLASS_ZIHINTPAUSE,
   INSN_CLASS_ZIMOP,
   INSN_CLASS_ZMMUL,
diff --git a/opcodes/riscv-opc.c b/opcodes/riscv-opc.c
index 8d5c574da6e..a1f847ae3ff 100644
--- a/opcodes/riscv-opc.c
+++ b/opcodes/riscv-opc.c
@@ -380,34 +380,34 @@  const struct riscv_opcode riscv_opcodes[] =
 {"prefetch.i",  0, INSN_CLASS_ZICBOP, "Wif(s)", MATCH_PREFETCH_I, MASK_PREFETCH_I, match_opcode, 0 },
 {"prefetch.r",  0, INSN_CLASS_ZICBOP, "Wif(s)", MATCH_PREFETCH_R, MASK_PREFETCH_R, match_opcode, 0 },
 {"prefetch.w",  0, INSN_CLASS_ZICBOP, "Wif(s)", MATCH_PREFETCH_W, MASK_PREFETCH_W, match_opcode, 0 },
-{"ntl.p1",      0, INSN_CLASS_ZIHINTNTL_AND_C, "", MATCH_C_NTL_P1, MASK_C_NTL_P1, match_opcode, INSN_ALIAS },
+{"ntl.p1",      0, INSN_CLASS_ZIHINTNTL_AND_ZCA, "", MATCH_C_NTL_P1, MASK_C_NTL_P1, match_opcode, INSN_ALIAS },
 {"ntl.p1",      0, INSN_CLASS_ZIHINTNTL,       "", MATCH_NTL_P1, MASK_NTL_P1, match_opcode, 0 },
-{"ntl.pall",    0, INSN_CLASS_ZIHINTNTL_AND_C, "", MATCH_C_NTL_PALL, MASK_C_NTL_PALL, match_opcode, INSN_ALIAS },
+{"ntl.pall",    0, INSN_CLASS_ZIHINTNTL_AND_ZCA, "", MATCH_C_NTL_PALL, MASK_C_NTL_PALL, match_opcode, INSN_ALIAS },
 {"ntl.pall",    0, INSN_CLASS_ZIHINTNTL,       "", MATCH_NTL_PALL, MASK_NTL_PALL, match_opcode, 0 },
-{"ntl.s1",      0, INSN_CLASS_ZIHINTNTL_AND_C, "", MATCH_C_NTL_S1, MASK_C_NTL_S1, match_opcode, INSN_ALIAS },
+{"ntl.s1",      0, INSN_CLASS_ZIHINTNTL_AND_ZCA, "", MATCH_C_NTL_S1, MASK_C_NTL_S1, match_opcode, INSN_ALIAS },
 {"ntl.s1",      0, INSN_CLASS_ZIHINTNTL,       "", MATCH_NTL_S1, MASK_NTL_S1, match_opcode, 0 },
-{"ntl.all",     0, INSN_CLASS_ZIHINTNTL_AND_C, "", MATCH_C_NTL_ALL, MASK_C_NTL_ALL, match_opcode, INSN_ALIAS },
+{"ntl.all",     0, INSN_CLASS_ZIHINTNTL_AND_ZCA, "", MATCH_C_NTL_ALL, MASK_C_NTL_ALL, match_opcode, INSN_ALIAS },
 {"ntl.all",     0, INSN_CLASS_ZIHINTNTL,       "", MATCH_NTL_ALL, MASK_NTL_ALL, match_opcode, 0 },
-{"c.ntl.p1",    0, INSN_CLASS_ZIHINTNTL_AND_C, "", MATCH_C_NTL_P1, MASK_C_NTL_P1, match_opcode, 0 },
-{"c.ntl.pall",  0, INSN_CLASS_ZIHINTNTL_AND_C, "", MATCH_C_NTL_PALL, MASK_C_NTL_PALL, match_opcode, 0 },
-{"c.ntl.s1",    0, INSN_CLASS_ZIHINTNTL_AND_C, "", MATCH_C_NTL_S1, MASK_C_NTL_S1, match_opcode, 0 },
-{"c.ntl.all",   0, INSN_CLASS_ZIHINTNTL_AND_C, "", MATCH_C_NTL_ALL, MASK_C_NTL_ALL, match_opcode, 0 },
+{"c.ntl.p1",    0, INSN_CLASS_ZIHINTNTL_AND_ZCA, "", MATCH_C_NTL_P1, MASK_C_NTL_P1, match_opcode, 0 },
+{"c.ntl.pall",  0, INSN_CLASS_ZIHINTNTL_AND_ZCA, "", MATCH_C_NTL_PALL, MASK_C_NTL_PALL, match_opcode, 0 },
+{"c.ntl.s1",    0, INSN_CLASS_ZIHINTNTL_AND_ZCA, "", MATCH_C_NTL_S1, MASK_C_NTL_S1, match_opcode, 0 },
+{"c.ntl.all",   0, INSN_CLASS_ZIHINTNTL_AND_ZCA, "", MATCH_C_NTL_ALL, MASK_C_NTL_ALL, match_opcode, 0 },
 {"pause",       0, INSN_CLASS_ZIHINTPAUSE, "", MATCH_PAUSE, MASK_PAUSE, match_opcode, 0 },
 
 /* Basic RVI instructions and aliases.  */
-{"unimp",       0, INSN_CLASS_C, "",          0, 0xffffU, match_opcode, INSN_ALIAS },
+{"unimp",       0, INSN_CLASS_ZCA, "",          0, 0xffffU, match_opcode, INSN_ALIAS },
 {"unimp",       0, INSN_CLASS_I, "",          MATCH_CSRRW|(CSR_CYCLE << OP_SH_CSR), 0xffffffffU,  match_opcode, 0 }, /* csrw cycle, x0  */
-{"ebreak",      0, INSN_CLASS_C, "",          MATCH_C_EBREAK, MASK_C_EBREAK, match_opcode, INSN_ALIAS },
+{"ebreak",      0, INSN_CLASS_ZCA, "",          MATCH_C_EBREAK, MASK_C_EBREAK, match_opcode, INSN_ALIAS },
 {"ebreak",      0, INSN_CLASS_I, "",          MATCH_EBREAK, MASK_EBREAK, match_opcode, 0 },
-{"sbreak",      0, INSN_CLASS_C, "",          MATCH_C_EBREAK, MASK_C_EBREAK, match_opcode, INSN_ALIAS },
+{"sbreak",      0, INSN_CLASS_ZCA, "",          MATCH_C_EBREAK, MASK_C_EBREAK, match_opcode, INSN_ALIAS },
 {"sbreak",      0, INSN_CLASS_I, "",          MATCH_EBREAK, MASK_EBREAK, match_opcode, INSN_ALIAS },
-{"ret",         0, INSN_CLASS_C, "",          MATCH_C_JR|(X_RA << OP_SH_RD), MASK_C_JR|MASK_RD, match_opcode, INSN_ALIAS|INSN_BRANCH },
+{"ret",         0, INSN_CLASS_ZCA, "",          MATCH_C_JR|(X_RA << OP_SH_RD), MASK_C_JR|MASK_RD, match_opcode, INSN_ALIAS|INSN_BRANCH },
 {"ret",         0, INSN_CLASS_I, "",          MATCH_JALR|(X_RA << OP_SH_RS1), MASK_JALR|MASK_RD|MASK_RS1|MASK_IMM, match_opcode, INSN_ALIAS|INSN_BRANCH },
-{"jr",          0, INSN_CLASS_C, "d",         MATCH_C_JR, MASK_C_JR, match_rd_nonzero, INSN_ALIAS|INSN_BRANCH },
+{"jr",          0, INSN_CLASS_ZCA, "d",         MATCH_C_JR, MASK_C_JR, match_rd_nonzero, INSN_ALIAS|INSN_BRANCH },
 {"jr",          0, INSN_CLASS_I, "s",         MATCH_JALR, MASK_JALR|MASK_RD|MASK_IMM, match_opcode, INSN_ALIAS|INSN_BRANCH },
 {"jr",          0, INSN_CLASS_I, "o(s)",      MATCH_JALR, MASK_JALR|MASK_RD, match_opcode, INSN_ALIAS|INSN_BRANCH },
 {"jr",          0, INSN_CLASS_I, "s,j",       MATCH_JALR, MASK_JALR|MASK_RD, match_opcode, INSN_ALIAS|INSN_BRANCH },
-{"jalr",        0, INSN_CLASS_C, "d",         MATCH_C_JALR, MASK_C_JALR, match_rd_nonzero, INSN_ALIAS|INSN_JSR },
+{"jalr",        0, INSN_CLASS_ZCA, "d",         MATCH_C_JALR, MASK_C_JALR, match_rd_nonzero, INSN_ALIAS|INSN_JSR },
 {"jalr",        0, INSN_CLASS_I, "s",         MATCH_JALR|(X_RA << OP_SH_RD), MASK_JALR|MASK_RD|MASK_IMM, match_opcode, INSN_ALIAS|INSN_JSR },
 {"jalr",        0, INSN_CLASS_I, "o(s)",      MATCH_JALR|(X_RA << OP_SH_RD), MASK_JALR|MASK_RD, match_opcode, INSN_ALIAS|INSN_JSR },
 {"jalr",        0, INSN_CLASS_I, "s,j",       MATCH_JALR|(X_RA << OP_SH_RD), MASK_JALR|MASK_RD, match_opcode, INSN_ALIAS|INSN_JSR },
@@ -415,40 +415,40 @@  const struct riscv_opcode riscv_opcodes[] =
 {"jalr",        0, INSN_CLASS_I, "d,o(s)",    MATCH_JALR, MASK_JALR, match_opcode, INSN_JSR },
 {"jalr",        0, INSN_CLASS_I, "d,s,1",     MATCH_JALR, MASK_JALR|MASK_IMM, match_opcode, INSN_JSR },
 {"jalr",        0, INSN_CLASS_I, "d,s,j",     MATCH_JALR, MASK_JALR, match_opcode, INSN_JSR },
-{"j",           0, INSN_CLASS_C, "Ca",        MATCH_C_J, MASK_C_J, match_opcode, INSN_ALIAS|INSN_BRANCH },
+{"j",           0, INSN_CLASS_ZCA, "Ca",        MATCH_C_J, MASK_C_J, match_opcode, INSN_ALIAS|INSN_BRANCH },
 {"j",           0, INSN_CLASS_I, "a",         MATCH_JAL, MASK_JAL|MASK_RD, match_opcode, INSN_ALIAS|INSN_BRANCH },
-{"jal",        32, INSN_CLASS_C, "Ca",        MATCH_C_JAL, MASK_C_JAL, match_opcode, INSN_ALIAS|INSN_JSR },
+{"jal",        32, INSN_CLASS_ZCA, "Ca",        MATCH_C_JAL, MASK_C_JAL, match_opcode, INSN_ALIAS|INSN_JSR },
 {"jal",         0, INSN_CLASS_I, "a",         MATCH_JAL|(X_RA << OP_SH_RD), MASK_JAL|MASK_RD, match_opcode, INSN_ALIAS|INSN_JSR },
 {"jal",         0, INSN_CLASS_I, "d,a",       MATCH_JAL, MASK_JAL, match_opcode, INSN_JSR },
 {"call",        0, INSN_CLASS_I, "d,c",       (X_T1 << OP_SH_RS1), (int) M_CALL, NULL, INSN_MACRO },
 {"call",        0, INSN_CLASS_I, "c",         (X_RA << OP_SH_RS1)|(X_RA << OP_SH_RD), (int) M_CALL, NULL, INSN_MACRO },
 {"tail",        0, INSN_CLASS_I, "c",         (X_T1 << OP_SH_RS1), (int) M_CALL, NULL, INSN_MACRO },
 {"jump",        0, INSN_CLASS_I, "c,s",       0, (int) M_CALL, match_rs1_nonzero, INSN_MACRO },
-{"nop",         0, INSN_CLASS_C, "",          MATCH_C_ADDI, 0xffff, match_opcode, INSN_ALIAS },
+{"nop",         0, INSN_CLASS_ZCA, "",          MATCH_C_ADDI, 0xffff, match_opcode, INSN_ALIAS },
 {"nop",         0, INSN_CLASS_I, "",          MATCH_ADDI, MASK_ADDI|MASK_RD|MASK_RS1|MASK_IMM, match_opcode, INSN_ALIAS },
-{"lui",         0, INSN_CLASS_C, "d,Cu",      MATCH_C_LUI, MASK_C_LUI, match_c_lui, INSN_ALIAS },
+{"lui",         0, INSN_CLASS_ZCA, "d,Cu",      MATCH_C_LUI, MASK_C_LUI, match_c_lui, INSN_ALIAS },
 {"lui",         0, INSN_CLASS_I, "d,u",       MATCH_LUI, MASK_LUI, match_opcode, 0 },
-{"li",          0, INSN_CLASS_C, "d,Cv",      MATCH_C_LUI, MASK_C_LUI, match_c_lui, INSN_ALIAS },
-{"li",          0, INSN_CLASS_C, "d,Co",      MATCH_C_LI, MASK_C_LI, match_rd_nonzero, INSN_ALIAS },
+{"li",          0, INSN_CLASS_ZCA, "d,Cv",      MATCH_C_LUI, MASK_C_LUI, match_c_lui, INSN_ALIAS },
+{"li",          0, INSN_CLASS_ZCA, "d,Co",      MATCH_C_LI, MASK_C_LI, match_rd_nonzero, INSN_ALIAS },
 {"li",          0, INSN_CLASS_I, "d,j",       MATCH_ADDI, MASK_ADDI|MASK_RS1, match_opcode, INSN_ALIAS }, /* addi  */
 {"li",          0, INSN_CLASS_I, "d,I",       0, (int) M_LI,  NULL, INSN_MACRO },
-{"mv",          0, INSN_CLASS_C, "d,CV",      MATCH_C_MV, MASK_C_MV, match_c_add, INSN_ALIAS },
+{"mv",          0, INSN_CLASS_ZCA, "d,CV",      MATCH_C_MV, MASK_C_MV, match_c_add, INSN_ALIAS },
 {"mv",          0, INSN_CLASS_I, "d,s",       MATCH_ADDI, MASK_ADDI|MASK_IMM, match_opcode, INSN_ALIAS },
-{"move",        0, INSN_CLASS_C, "d,CV",      MATCH_C_MV, MASK_C_MV, match_c_add, INSN_ALIAS },
+{"move",        0, INSN_CLASS_ZCA, "d,CV",      MATCH_C_MV, MASK_C_MV, match_c_add, INSN_ALIAS },
 {"move",        0, INSN_CLASS_I, "d,s",       MATCH_ADDI, MASK_ADDI|MASK_IMM, match_opcode, INSN_ALIAS },
 {"zext.b",      0, INSN_CLASS_ZCB, "Cs,Cw",   MATCH_C_ZEXT_B, MASK_C_ZEXT_B, match_opcode, INSN_ALIAS },
 {"zext.b",      0, INSN_CLASS_I, "d,s",       MATCH_ANDI|ENCODE_ITYPE_IMM (255), MASK_ANDI | MASK_IMM, match_opcode, INSN_ALIAS },
 {"andi",        0, INSN_CLASS_ZCB, "Cs,Cw,Wcf",MATCH_C_ZEXT_B, MASK_C_ZEXT_B, match_opcode, INSN_ALIAS },
-{"andi",        0, INSN_CLASS_C, "Cs,Cw,Co",  MATCH_C_ANDI, MASK_C_ANDI, match_opcode, INSN_ALIAS },
+{"andi",        0, INSN_CLASS_ZCA, "Cs,Cw,Co",  MATCH_C_ANDI, MASK_C_ANDI, match_opcode, INSN_ALIAS },
 {"andi",        0, INSN_CLASS_I, "d,s,j",     MATCH_ANDI, MASK_ANDI, match_opcode, 0 },
-{"and",         0, INSN_CLASS_C, "Cs,Cw,Ct",  MATCH_C_AND, MASK_C_AND, match_opcode, INSN_ALIAS },
-{"and",         0, INSN_CLASS_C, "Cs,Ct,Cw",  MATCH_C_AND, MASK_C_AND, match_opcode, INSN_ALIAS },
-{"and",         0, INSN_CLASS_C, "Cs,Cw,Co",  MATCH_C_ANDI, MASK_C_ANDI, match_opcode, INSN_ALIAS },
+{"and",         0, INSN_CLASS_ZCA, "Cs,Cw,Ct",  MATCH_C_AND, MASK_C_AND, match_opcode, INSN_ALIAS },
+{"and",         0, INSN_CLASS_ZCA, "Cs,Ct,Cw",  MATCH_C_AND, MASK_C_AND, match_opcode, INSN_ALIAS },
+{"and",         0, INSN_CLASS_ZCA, "Cs,Cw,Co",  MATCH_C_ANDI, MASK_C_ANDI, match_opcode, INSN_ALIAS },
 {"and",         0, INSN_CLASS_I, "d,s,t",     MATCH_AND, MASK_AND, match_opcode, 0 },
 {"and",         0, INSN_CLASS_I, "d,s,j",     MATCH_ANDI, MASK_ANDI, match_opcode, INSN_ALIAS },
-{"beqz",        0, INSN_CLASS_C, "Cs,Cp",     MATCH_C_BEQZ, MASK_C_BEQZ, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
+{"beqz",        0, INSN_CLASS_ZCA, "Cs,Cp",     MATCH_C_BEQZ, MASK_C_BEQZ, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
 {"beqz",        0, INSN_CLASS_I, "s,p",       MATCH_BEQ, MASK_BEQ|MASK_RS2, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
-{"beq",         0, INSN_CLASS_C, "Cs,Cz,Cp",  MATCH_C_BEQZ, MASK_C_BEQZ, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
+{"beq",         0, INSN_CLASS_ZCA, "Cs,Cz,Cp",  MATCH_C_BEQZ, MASK_C_BEQZ, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
 {"beq",         0, INSN_CLASS_I, "s,t,p",     MATCH_BEQ, MASK_BEQ, match_opcode, INSN_CONDBRANCH },
 {"blez",        0, INSN_CLASS_I, "t,p",       MATCH_BGE, MASK_BGE|MASK_RS1, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
 {"bgez",        0, INSN_CLASS_I, "s,p",       MATCH_BGE, MASK_BGE|MASK_RS2, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
@@ -462,23 +462,23 @@  const struct riscv_opcode riscv_opcodes[] =
 {"bltu",        0, INSN_CLASS_I, "s,t,p",     MATCH_BLTU, MASK_BLTU, match_opcode, INSN_CONDBRANCH },
 {"bgt",         0, INSN_CLASS_I, "t,s,p",     MATCH_BLT, MASK_BLT, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
 {"bgtu",        0, INSN_CLASS_I, "t,s,p",     MATCH_BLTU, MASK_BLTU, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
-{"bnez",        0, INSN_CLASS_C, "Cs,Cp",     MATCH_C_BNEZ, MASK_C_BNEZ, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
+{"bnez",        0, INSN_CLASS_ZCA, "Cs,Cp",     MATCH_C_BNEZ, MASK_C_BNEZ, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
 {"bnez",        0, INSN_CLASS_I, "s,p",       MATCH_BNE, MASK_BNE|MASK_RS2, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
-{"bne",         0, INSN_CLASS_C, "Cs,Cz,Cp",  MATCH_C_BNEZ, MASK_C_BNEZ, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
+{"bne",         0, INSN_CLASS_ZCA, "Cs,Cz,Cp",  MATCH_C_BNEZ, MASK_C_BNEZ, match_opcode, INSN_ALIAS|INSN_CONDBRANCH },
 {"bne",         0, INSN_CLASS_I, "s,t,p",     MATCH_BNE, MASK_BNE, match_opcode, INSN_CONDBRANCH },
-{"addi",        0, INSN_CLASS_C, "Ct,Cc,CK",  MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN, match_c_addi4spn, INSN_ALIAS },
-{"addi",        0, INSN_CLASS_C, "d,CU,Cj",   MATCH_C_ADDI, MASK_C_ADDI, match_rd_nonzero, INSN_ALIAS },
-{"addi",        0, INSN_CLASS_C, "d,CU,z",    MATCH_C_NOP, MASK_C_ADDI|MASK_RVC_IMM, match_c_nop, INSN_ALIAS },
-{"addi",        0, INSN_CLASS_C, "Cc,Cc,CL",  MATCH_C_ADDI16SP, MASK_C_ADDI16SP, match_c_addi16sp, INSN_ALIAS },
-{"addi",        0, INSN_CLASS_C, "d,Cz,Co",   MATCH_C_LI, MASK_C_LI, match_rd_nonzero, INSN_ALIAS },
-{"addi",        0, INSN_CLASS_C, "d,CV,z",    MATCH_C_MV, MASK_C_MV, match_c_add, INSN_ALIAS },
+{"addi",        0, INSN_CLASS_ZCA, "Ct,Cc,CK",  MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN, match_c_addi4spn, INSN_ALIAS },
+{"addi",        0, INSN_CLASS_ZCA, "d,CU,Cj",   MATCH_C_ADDI, MASK_C_ADDI, match_rd_nonzero, INSN_ALIAS },
+{"addi",        0, INSN_CLASS_ZCA, "d,CU,z",    MATCH_C_NOP, MASK_C_ADDI|MASK_RVC_IMM, match_c_nop, INSN_ALIAS },
+{"addi",        0, INSN_CLASS_ZCA, "Cc,Cc,CL",  MATCH_C_ADDI16SP, MASK_C_ADDI16SP, match_c_addi16sp, INSN_ALIAS },
+{"addi",        0, INSN_CLASS_ZCA, "d,Cz,Co",   MATCH_C_LI, MASK_C_LI, match_rd_nonzero, INSN_ALIAS },
+{"addi",        0, INSN_CLASS_ZCA, "d,CV,z",    MATCH_C_MV, MASK_C_MV, match_c_add, INSN_ALIAS },
 {"addi",        0, INSN_CLASS_I, "d,s,j",     MATCH_ADDI, MASK_ADDI, match_opcode, 0 },
-{"add",         0, INSN_CLASS_C, "d,CU,CV",   MATCH_C_ADD, MASK_C_ADD, match_c_add, INSN_ALIAS },
-{"add",         0, INSN_CLASS_C, "d,CV,CU",   MATCH_C_ADD, MASK_C_ADD, match_c_add, INSN_ALIAS },
-{"add",         0, INSN_CLASS_C, "d,CU,Co",   MATCH_C_ADDI, MASK_C_ADDI, match_rd_nonzero, INSN_ALIAS },
-{"add",         0, INSN_CLASS_C, "Ct,Cc,CK",  MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN, match_c_addi4spn, INSN_ALIAS },
-{"add",         0, INSN_CLASS_C, "Cc,Cc,CL",  MATCH_C_ADDI16SP, MASK_C_ADDI16SP, match_c_addi16sp, INSN_ALIAS },
-{"add",         0, INSN_CLASS_C, "d,Cz,CV",   MATCH_C_MV, MASK_C_MV, match_c_add, INSN_ALIAS },
+{"add",         0, INSN_CLASS_ZCA, "d,CU,CV",   MATCH_C_ADD, MASK_C_ADD, match_c_add, INSN_ALIAS },
+{"add",         0, INSN_CLASS_ZCA, "d,CV,CU",   MATCH_C_ADD, MASK_C_ADD, match_c_add, INSN_ALIAS },
+{"add",         0, INSN_CLASS_ZCA, "d,CU,Co",   MATCH_C_ADDI, MASK_C_ADDI, match_rd_nonzero, INSN_ALIAS },
+{"add",         0, INSN_CLASS_ZCA, "Ct,Cc,CK",  MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN, match_c_addi4spn, INSN_ALIAS },
+{"add",         0, INSN_CLASS_ZCA, "Cc,Cc,CL",  MATCH_C_ADDI16SP, MASK_C_ADDI16SP, match_c_addi16sp, INSN_ALIAS },
+{"add",         0, INSN_CLASS_ZCA, "d,Cz,CV",   MATCH_C_MV, MASK_C_MV, match_c_add, INSN_ALIAS },
 {"add",         0, INSN_CLASS_I, "d,s,t",     MATCH_ADD, MASK_ADD, match_opcode, 0 },
 {"add",         0, INSN_CLASS_I, "d,s,t,1",   MATCH_ADD, MASK_ADD, match_opcode, 0 },
 {"add",         0, INSN_CLASS_I, "d,s,j",     MATCH_ADDI, MASK_ADDI, match_opcode, INSN_ALIAS },
@@ -488,22 +488,22 @@  const struct riscv_opcode riscv_opcodes[] =
 {"la.tls.gd",   0, INSN_CLASS_I, "d,A",       0, (int) M_LA_TLS_GD, NULL, INSN_MACRO },
 {"la.tls.ie",   0, INSN_CLASS_I, "d,A",       0, (int) M_LA_TLS_IE, match_rd_nonzero, INSN_MACRO },
 {"neg",         0, INSN_CLASS_I, "d,t",       MATCH_SUB, MASK_SUB|MASK_RS1, match_opcode, INSN_ALIAS }, /* sub 0  */
-{"slli",        0, INSN_CLASS_C, "d,CU,C>",   MATCH_C_SLLI, MASK_C_SLLI, match_slli_as_c_slli, INSN_ALIAS },
+{"slli",        0, INSN_CLASS_ZCA, "d,CU,C>",   MATCH_C_SLLI, MASK_C_SLLI, match_slli_as_c_slli, INSN_ALIAS },
 {"slli",        0, INSN_CLASS_I, "d,s,>",     MATCH_SLLI, MASK_SLLI, match_opcode, 0 },
-{"sll",         0, INSN_CLASS_C, "d,CU,C>",   MATCH_C_SLLI, MASK_C_SLLI, match_slli_as_c_slli, INSN_ALIAS },
+{"sll",         0, INSN_CLASS_ZCA, "d,CU,C>",   MATCH_C_SLLI, MASK_C_SLLI, match_slli_as_c_slli, INSN_ALIAS },
 {"sll",         0, INSN_CLASS_I, "d,s,t",     MATCH_SLL, MASK_SLL, match_opcode, 0 },
 {"sll",         0, INSN_CLASS_I, "d,s,>",     MATCH_SLLI, MASK_SLLI, match_opcode, INSN_ALIAS },
-{"srli",        0, INSN_CLASS_C, "Cs,Cw,C>",  MATCH_C_SRLI, MASK_C_SRLI, match_srxi_as_c_srxi, INSN_ALIAS },
+{"srli",        0, INSN_CLASS_ZCA, "Cs,Cw,C>",  MATCH_C_SRLI, MASK_C_SRLI, match_srxi_as_c_srxi, INSN_ALIAS },
 {"srli",        0, INSN_CLASS_I, "d,s,>",     MATCH_SRLI, MASK_SRLI, match_opcode, 0 },
-{"srl",         0, INSN_CLASS_C, "Cs,Cw,C>",  MATCH_C_SRLI, MASK_C_SRLI, match_srxi_as_c_srxi, INSN_ALIAS },
+{"srl",         0, INSN_CLASS_ZCA, "Cs,Cw,C>",  MATCH_C_SRLI, MASK_C_SRLI, match_srxi_as_c_srxi, INSN_ALIAS },
 {"srl",         0, INSN_CLASS_I, "d,s,t",     MATCH_SRL, MASK_SRL, match_opcode, 0 },
 {"srl",         0, INSN_CLASS_I, "d,s,>",     MATCH_SRLI, MASK_SRLI, match_opcode, INSN_ALIAS },
-{"srai",        0, INSN_CLASS_C, "Cs,Cw,C>",  MATCH_C_SRAI, MASK_C_SRAI, match_srxi_as_c_srxi, INSN_ALIAS },
+{"srai",        0, INSN_CLASS_ZCA, "Cs,Cw,C>",  MATCH_C_SRAI, MASK_C_SRAI, match_srxi_as_c_srxi, INSN_ALIAS },
 {"srai",        0, INSN_CLASS_I, "d,s,>",     MATCH_SRAI, MASK_SRAI, match_opcode, 0 },
-{"sra",         0, INSN_CLASS_C, "Cs,Cw,C>",  MATCH_C_SRAI, MASK_C_SRAI, match_srxi_as_c_srxi, INSN_ALIAS },
+{"sra",         0, INSN_CLASS_ZCA, "Cs,Cw,C>",  MATCH_C_SRAI, MASK_C_SRAI, match_srxi_as_c_srxi, INSN_ALIAS },
 {"sra",         0, INSN_CLASS_I, "d,s,t",     MATCH_SRA, MASK_SRA, match_opcode, 0 },
 {"sra",         0, INSN_CLASS_I, "d,s,>",     MATCH_SRAI, MASK_SRAI, match_opcode, INSN_ALIAS },
-{"sub",         0, INSN_CLASS_C, "Cs,Cw,Ct",  MATCH_C_SUB, MASK_C_SUB, match_opcode, INSN_ALIAS },
+{"sub",         0, INSN_CLASS_ZCA, "Cs,Cw,Ct",  MATCH_C_SUB, MASK_C_SUB, match_opcode, INSN_ALIAS },
 {"sub",         0, INSN_CLASS_I, "d,s,t",     MATCH_SUB, MASK_SUB, match_opcode, 0 },
 {"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 },
@@ -516,16 +516,16 @@  const struct riscv_opcode riscv_opcodes[] =
 {"lhu",         0, INSN_CLASS_ZCB, "Ct,Wch(Cs)", MATCH_C_LHU, MASK_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_C, "d,Cm(Cc)",  MATCH_C_LWSP, MASK_C_LWSP, match_rd_nonzero, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
-{"lw",          0, INSN_CLASS_C, "Ct,Ck(Cs)", MATCH_C_LW, MASK_C_LW, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
+{"lw",          0, INSN_CLASS_ZCA, "d,Cm(Cc)",  MATCH_C_LWSP, MASK_C_LWSP, match_rd_nonzero, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
+{"lw",          0, INSN_CLASS_ZCA, "Ct,Ck(Cs)", MATCH_C_LW, MASK_C_LW, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
 {"lw",          0, INSN_CLASS_I, "d,o(s)",    MATCH_LW, MASK_LW, match_opcode, INSN_DREF|INSN_4_BYTE },
 {"lw",          0, INSN_CLASS_I, "d,A",       0, (int) M_Lx, match_rd_nonzero, INSN_MACRO },
 {"not",         0, INSN_CLASS_ZCB,  "Cs,Cw",  MATCH_C_NOT, MASK_C_NOT, match_opcode, INSN_ALIAS },
 {"not",         0, INSN_CLASS_I, "d,s",       MATCH_XORI|MASK_IMM, MASK_XORI|MASK_IMM, match_opcode, INSN_ALIAS },
 {"ori",         0, INSN_CLASS_I, "d,s,j",     MATCH_ORI, MASK_ORI, match_opcode, 0 },
 {"or",          0, INSN_CLASS_I, "d,s,j",     MATCH_ORI, MASK_ORI, match_opcode, INSN_ALIAS },
-{"or",          0, INSN_CLASS_C, "Cs,Cw,Ct",  MATCH_C_OR, MASK_C_OR, match_opcode, INSN_ALIAS },
-{"or",          0, INSN_CLASS_C, "Cs,Ct,Cw",  MATCH_C_OR, MASK_C_OR, match_opcode, INSN_ALIAS },
+{"or",          0, INSN_CLASS_ZCA, "Cs,Cw,Ct",  MATCH_C_OR, MASK_C_OR, match_opcode, INSN_ALIAS },
+{"or",          0, INSN_CLASS_ZCA, "Cs,Ct,Cw",  MATCH_C_OR, MASK_C_OR, match_opcode, INSN_ALIAS },
 {"or",          0, INSN_CLASS_I, "d,s,t",     MATCH_OR, MASK_OR, match_opcode, 0 },
 {"auipc",       0, INSN_CLASS_I, "d,u",       MATCH_AUIPC, MASK_AUIPC, match_opcode, 0 },
 {"seqz",        0, INSN_CLASS_I, "d,s",       MATCH_SLTIU|ENCODE_ITYPE_IMM (1), MASK_SLTIU | MASK_IMM, match_opcode, INSN_ALIAS },
@@ -546,8 +546,8 @@  const struct riscv_opcode riscv_opcodes[] =
 {"sh",          0, INSN_CLASS_ZCB, "Ct,Wch(Cs)", MATCH_C_SH, MASK_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_C, "CV,CM(Cc)", MATCH_C_SWSP, MASK_C_SWSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
-{"sw",          0, INSN_CLASS_C, "Ct,Ck(Cs)", MATCH_C_SW, MASK_C_SW, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
+{"sw",          0, INSN_CLASS_ZCA, "CV,CM(Cc)", MATCH_C_SWSP, MASK_C_SWSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
+{"sw",          0, INSN_CLASS_ZCA, "Ct,Ck(Cs)", MATCH_C_SW, MASK_C_SW, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
 {"sw",          0, INSN_CLASS_I, "t,q(s)",    MATCH_SW, MASK_SW, match_opcode, INSN_DREF|INSN_4_BYTE },
 {"sw",          0, INSN_CLASS_I, "t,A,s",     0, (int) M_Sx_FSx, match_rs1_nonzero, INSN_MACRO },
 {"fence",       0, INSN_CLASS_I, "",          MATCH_FENCE|MASK_PRED|MASK_SUCC, MASK_FENCE|MASK_RD|MASK_RS1|MASK_IMM, match_opcode, INSN_ALIAS },
@@ -564,26 +564,26 @@  const struct riscv_opcode riscv_opcodes[] =
 {"scall",       0, INSN_CLASS_I, "",          MATCH_SCALL, MASK_SCALL, match_opcode, 0 },
 {"xori",        0, INSN_CLASS_I, "d,s,j",     MATCH_XORI, MASK_XORI, match_opcode, 0 },
 {"xor",         0, INSN_CLASS_I, "d,s,j",     MATCH_XORI, MASK_XORI, match_opcode, INSN_ALIAS },
-{"xor",         0, INSN_CLASS_C, "Cs,Cw,Ct",  MATCH_C_XOR, MASK_C_XOR, match_opcode, INSN_ALIAS },
-{"xor",         0, INSN_CLASS_C, "Cs,Ct,Cw",  MATCH_C_XOR, MASK_C_XOR, match_opcode, INSN_ALIAS },
+{"xor",         0, INSN_CLASS_ZCA, "Cs,Cw,Ct",  MATCH_C_XOR, MASK_C_XOR, match_opcode, INSN_ALIAS },
+{"xor",         0, INSN_CLASS_ZCA, "Cs,Ct,Cw",  MATCH_C_XOR, MASK_C_XOR, match_opcode, INSN_ALIAS },
 {"xor",         0, INSN_CLASS_I, "d,s,t",     MATCH_XOR, MASK_XOR, match_opcode, 0 },
 {"lwu",        64, INSN_CLASS_I, "d,o(s)",    MATCH_LWU, MASK_LWU, match_opcode, INSN_DREF|INSN_4_BYTE },
 {"lwu",        64, INSN_CLASS_I, "d,A",       0, (int) M_Lx, match_rd_nonzero, INSN_MACRO },
-{"ld",         64, INSN_CLASS_C, "d,Cn(Cc)",  MATCH_C_LDSP, MASK_C_LDSP, match_rd_nonzero, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
-{"ld",         64, INSN_CLASS_C, "Ct,Cl(Cs)", MATCH_C_LD, MASK_C_LD, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
+{"ld",         64, INSN_CLASS_ZCA, "d,Cn(Cc)",  MATCH_C_LDSP, MASK_C_LDSP, match_rd_nonzero, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
+{"ld",         64, INSN_CLASS_ZCA, "Ct,Cl(Cs)", MATCH_C_LD, MASK_C_LD, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
 {"ld",         64, INSN_CLASS_I, "d,o(s)",    MATCH_LD, MASK_LD, match_opcode, INSN_DREF|INSN_8_BYTE },
 {"ld",         64, INSN_CLASS_I, "d,A",       0, (int) M_Lx, match_rd_nonzero, INSN_MACRO },
-{"sd",         64, INSN_CLASS_C, "CV,CN(Cc)", MATCH_C_SDSP, MASK_C_SDSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
-{"sd",         64, INSN_CLASS_C, "Ct,Cl(Cs)", MATCH_C_SD, MASK_C_SD, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
+{"sd",         64, INSN_CLASS_ZCA, "CV,CN(Cc)", MATCH_C_SDSP, MASK_C_SDSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
+{"sd",         64, INSN_CLASS_ZCA, "Ct,Cl(Cs)", MATCH_C_SD, MASK_C_SD, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
 {"sd",         64, INSN_CLASS_I, "t,q(s)",    MATCH_SD, MASK_SD, match_opcode, INSN_DREF|INSN_8_BYTE },
 {"sd",         64, INSN_CLASS_I, "t,A,s",     0, (int) M_Sx_FSx, match_rs1_nonzero, INSN_MACRO },
-{"sext.w",     64, INSN_CLASS_C, "d,CU",      MATCH_C_ADDIW, MASK_C_ADDIW|MASK_RVC_IMM, match_rd_nonzero, INSN_ALIAS },
+{"sext.w",     64, INSN_CLASS_ZCA, "d,CU",      MATCH_C_ADDIW, MASK_C_ADDIW|MASK_RVC_IMM, match_rd_nonzero, INSN_ALIAS },
 {"sext.w",     64, INSN_CLASS_I, "d,s",       MATCH_ADDIW, MASK_ADDIW|MASK_IMM, match_opcode, INSN_ALIAS },
-{"addiw",      64, INSN_CLASS_C, "d,CU,Co",   MATCH_C_ADDIW, MASK_C_ADDIW, match_rd_nonzero, INSN_ALIAS },
+{"addiw",      64, INSN_CLASS_ZCA, "d,CU,Co",   MATCH_C_ADDIW, MASK_C_ADDIW, match_rd_nonzero, INSN_ALIAS },
 {"addiw",      64, INSN_CLASS_I, "d,s,j",     MATCH_ADDIW, MASK_ADDIW, match_opcode, 0 },
-{"addw",       64, INSN_CLASS_C, "Cs,Cw,Ct",  MATCH_C_ADDW, MASK_C_ADDW, match_opcode, INSN_ALIAS },
-{"addw",       64, INSN_CLASS_C, "Cs,Ct,Cw",  MATCH_C_ADDW, MASK_C_ADDW, match_opcode, INSN_ALIAS },
-{"addw",       64, INSN_CLASS_C, "d,CU,Co",   MATCH_C_ADDIW, MASK_C_ADDIW, match_rd_nonzero, INSN_ALIAS },
+{"addw",       64, INSN_CLASS_ZCA, "Cs,Cw,Ct",  MATCH_C_ADDW, MASK_C_ADDW, match_opcode, INSN_ALIAS },
+{"addw",       64, INSN_CLASS_ZCA, "Cs,Ct,Cw",  MATCH_C_ADDW, MASK_C_ADDW, match_opcode, INSN_ALIAS },
+{"addw",       64, INSN_CLASS_ZCA, "d,CU,Co",   MATCH_C_ADDIW, MASK_C_ADDIW, match_rd_nonzero, INSN_ALIAS },
 {"addw",       64, INSN_CLASS_I, "d,s,t",     MATCH_ADDW, MASK_ADDW, match_opcode, 0 },
 {"addw",       64, INSN_CLASS_I, "d,s,j",     MATCH_ADDIW, MASK_ADDIW, match_opcode, INSN_ALIAS },
 {"negw",       64, INSN_CLASS_I, "d,t",       MATCH_SUBW, MASK_SUBW|MASK_RS1, match_opcode, INSN_ALIAS }, /* sub 0  */
@@ -596,7 +596,7 @@  const struct riscv_opcode riscv_opcodes[] =
 {"sraiw",      64, INSN_CLASS_I, "d,s,<",     MATCH_SRAIW, MASK_SRAIW, match_opcode, 0 },
 {"sraw",       64, INSN_CLASS_I, "d,s,t",     MATCH_SRAW, MASK_SRAW, match_opcode, 0 },
 {"sraw",       64, INSN_CLASS_I, "d,s,<",     MATCH_SRAIW, MASK_SRAIW, match_opcode, INSN_ALIAS },
-{"subw",       64, INSN_CLASS_C, "Cs,Cw,Ct",  MATCH_C_SUBW, MASK_C_SUBW, match_opcode, INSN_ALIAS },
+{"subw",       64, INSN_CLASS_ZCA, "Cs,Cw,Ct",  MATCH_C_SUBW, MASK_C_SUBW, match_opcode, INSN_ALIAS },
 {"subw",       64, INSN_CLASS_I, "d,s,t",     MATCH_SUBW, MASK_SUBW, match_opcode, 0 },
 
 /* Atomic memory operation instruction subset.  */
@@ -892,12 +892,12 @@  const struct riscv_opcode riscv_opcodes[] =
 {"fsflags",    0, INSN_CLASS_F_INX,   "d,s",       MATCH_FSFLAGS, MASK_FSFLAGS, match_opcode, INSN_ALIAS },
 {"fsflagsi",   0, INSN_CLASS_F_INX,   "d,Z",       MATCH_FSFLAGSI, MASK_FSFLAGSI, match_opcode, INSN_ALIAS },
 {"fsflagsi",   0, INSN_CLASS_F_INX,   "Z",         MATCH_FSFLAGSI, MASK_FSFLAGSI|MASK_RD, match_opcode, INSN_ALIAS },
-{"flw",       32, INSN_CLASS_F_AND_C, "D,Cm(Cc)",  MATCH_C_FLWSP, MASK_C_FLWSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
-{"flw",       32, INSN_CLASS_F_AND_C, "CD,Ck(Cs)", MATCH_C_FLW, MASK_C_FLW, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
+{"flw",       32, INSN_CLASS_ZCF, "D,Cm(Cc)",  MATCH_C_FLWSP, MASK_C_FLWSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
+{"flw",       32, INSN_CLASS_ZCF, "CD,Ck(Cs)", MATCH_C_FLW, MASK_C_FLW, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
 {"flw",        0, INSN_CLASS_F,   "D,o(s)",    MATCH_FLW, MASK_FLW, match_opcode, INSN_DREF|INSN_4_BYTE },
 {"flw",        0, INSN_CLASS_F,   "D,A,s",     0, (int) M_FLx, match_rs1_nonzero, INSN_MACRO },
-{"fsw",       32, INSN_CLASS_F_AND_C, "CT,CM(Cc)", MATCH_C_FSWSP, MASK_C_FSWSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
-{"fsw",       32, INSN_CLASS_F_AND_C, "CD,Ck(Cs)", MATCH_C_FSW, MASK_C_FSW, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
+{"fsw",       32, INSN_CLASS_ZCF, "CT,CM(Cc)", MATCH_C_FSWSP, MASK_C_FSWSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
+{"fsw",       32, INSN_CLASS_ZCF, "CD,Ck(Cs)", MATCH_C_FSW, MASK_C_FSW, match_opcode, INSN_ALIAS|INSN_DREF|INSN_4_BYTE },
 {"fsw",        0, INSN_CLASS_F,   "T,q(s)",    MATCH_FSW, MASK_FSW, match_opcode, INSN_DREF|INSN_4_BYTE },
 {"fsw",        0, INSN_CLASS_F,   "T,A,s",     0, (int) M_Sx_FSx, match_rs1_nonzero, INSN_MACRO },
 {"fmv.x.w",    0, INSN_CLASS_F,   "d,S",       MATCH_FMV_X_S, MASK_FMV_X_S, match_opcode, 0 },
@@ -954,12 +954,12 @@  const struct riscv_opcode riscv_opcodes[] =
 {"fcvt.s.lu", 64, INSN_CLASS_F_INX,   "D,s,m",     MATCH_FCVT_S_LU, MASK_FCVT_S_LU, match_opcode, 0 },
 
 /* Double-precision floating-point instruction subset.  */
-{"fld",        0, INSN_CLASS_D_AND_C, "D,Cn(Cc)",  MATCH_C_FLDSP, MASK_C_FLDSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
-{"fld",        0, INSN_CLASS_D_AND_C, "CD,Cl(Cs)", MATCH_C_FLD, MASK_C_FLD, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
+{"fld",        0, INSN_CLASS_ZCD, "D,Cn(Cc)",  MATCH_C_FLDSP, MASK_C_FLDSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
+{"fld",        0, INSN_CLASS_ZCD, "CD,Cl(Cs)", MATCH_C_FLD, MASK_C_FLD, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
 {"fld",        0, INSN_CLASS_D,   "D,o(s)",    MATCH_FLD, MASK_FLD, match_opcode, INSN_DREF|INSN_8_BYTE },
 {"fld",        0, INSN_CLASS_D,   "D,A,s",     0, (int) M_FLx, match_rs1_nonzero, INSN_MACRO },
-{"fsd",        0, INSN_CLASS_D_AND_C, "CT,CN(Cc)", MATCH_C_FSDSP, MASK_C_FSDSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
-{"fsd",        0, INSN_CLASS_D_AND_C, "CD,Cl(Cs)", MATCH_C_FSD, MASK_C_FSD, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
+{"fsd",        0, INSN_CLASS_ZCD, "CT,CN(Cc)", MATCH_C_FSDSP, MASK_C_FSDSP, match_opcode, INSN_ALIAS|INSN_DREF|INSN_8_BYTE },
+{"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 },
@@ -1073,53 +1073,53 @@  const struct riscv_opcode riscv_opcodes[] =
 {"fcvt.q.lu", 64, INSN_CLASS_Q_INX,   "D,s,m",     MATCH_FCVT_Q_LU, MASK_FCVT_Q_LU, match_opcode, 0 },
 
 /* Compressed instructions.  */
-{"c.unimp",    0, INSN_CLASS_C,   "",          0, 0xffffU,  match_opcode, 0 },
-{"c.ebreak",   0, INSN_CLASS_C,   "",          MATCH_C_EBREAK, MASK_C_EBREAK, match_opcode, 0 },
-{"c.jr",       0, INSN_CLASS_C,   "d",         MATCH_C_JR, MASK_C_JR, match_rd_nonzero, INSN_BRANCH },
-{"c.jalr",     0, INSN_CLASS_C,   "d",         MATCH_C_JALR, MASK_C_JALR, match_rd_nonzero, INSN_JSR },
-{"c.j",        0, INSN_CLASS_C,   "Ca",        MATCH_C_J, MASK_C_J, match_opcode, INSN_BRANCH },
-{"c.jal",     32, INSN_CLASS_C,   "Ca",        MATCH_C_JAL, MASK_C_JAL, match_opcode, INSN_JSR },
-{"c.beqz",     0, INSN_CLASS_C,   "Cs,Cp",     MATCH_C_BEQZ, MASK_C_BEQZ, match_opcode, INSN_CONDBRANCH },
-{"c.bnez",     0, INSN_CLASS_C,   "Cs,Cp",     MATCH_C_BNEZ, MASK_C_BNEZ, match_opcode, INSN_CONDBRANCH },
-{"c.lwsp",     0, INSN_CLASS_C,   "d,Cm(Cc)",  MATCH_C_LWSP, MASK_C_LWSP, match_rd_nonzero, 0 },
-{"c.lw",       0, INSN_CLASS_C,   "Ct,Ck(Cs)", MATCH_C_LW, MASK_C_LW, match_opcode, INSN_DREF|INSN_4_BYTE },
-{"c.swsp",     0, INSN_CLASS_C,   "CV,CM(Cc)", MATCH_C_SWSP, MASK_C_SWSP, match_opcode, INSN_DREF|INSN_4_BYTE },
-{"c.sw",       0, INSN_CLASS_C,   "Ct,Ck(Cs)", MATCH_C_SW, MASK_C_SW, match_opcode, INSN_DREF|INSN_4_BYTE },
-{"c.nop",      0, INSN_CLASS_C,   "",          MATCH_C_ADDI, 0xffff, match_opcode, INSN_ALIAS },
-{"c.nop",      0, INSN_CLASS_C,   "Cj",        MATCH_C_ADDI, MASK_C_ADDI|MASK_RD, match_opcode, INSN_ALIAS },
-{"c.mv",       0, INSN_CLASS_C,   "d,CV",      MATCH_C_MV, MASK_C_MV, match_c_add_with_hint, 0 },
-{"c.lui",      0, INSN_CLASS_C,   "d,Cu",      MATCH_C_LUI, MASK_C_LUI, match_c_lui_with_hint, 0 },
-{"c.li",       0, INSN_CLASS_C,   "d,Co",      MATCH_C_LI, MASK_C_LI, match_opcode, 0 },
-{"c.addi4spn", 0, INSN_CLASS_C,   "Ct,Cc,CK",  MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN, match_c_addi4spn, 0 },
-{"c.addi16sp", 0, INSN_CLASS_C,   "Cc,CL",     MATCH_C_ADDI16SP, MASK_C_ADDI16SP, match_c_addi16sp, 0 },
-{"c.addi",     0, INSN_CLASS_C,   "d,Co",      MATCH_C_ADDI, MASK_C_ADDI, match_opcode, 0 },
-{"c.add",      0, INSN_CLASS_C,   "d,CV",      MATCH_C_ADD, MASK_C_ADD, match_c_add_with_hint, 0 },
-{"c.sub",      0, INSN_CLASS_C,   "Cs,Ct",     MATCH_C_SUB, MASK_C_SUB, match_opcode, 0 },
-{"c.and",      0, INSN_CLASS_C,   "Cs,Ct",     MATCH_C_AND, MASK_C_AND, match_opcode, 0 },
-{"c.or",       0, INSN_CLASS_C,   "Cs,Ct",     MATCH_C_OR, MASK_C_OR, match_opcode, 0 },
-{"c.xor",      0, INSN_CLASS_C,   "Cs,Ct",     MATCH_C_XOR, MASK_C_XOR, match_opcode, 0 },
-{"c.slli",     0, INSN_CLASS_C,   "d,C>",      MATCH_C_SLLI, MASK_C_SLLI, match_c_slli, 0 },
-{"c.srli",     0, INSN_CLASS_C,   "Cs,C>",     MATCH_C_SRLI, MASK_C_SRLI, match_c_slli, 0 },
-{"c.srai",     0, INSN_CLASS_C,   "Cs,C>",     MATCH_C_SRAI, MASK_C_SRAI, match_c_slli, 0 },
-{"c.slli64",   0, INSN_CLASS_C,   "d",         MATCH_C_SLLI64, MASK_C_SLLI64, match_c_slli64, 0 },
-{"c.srli64",   0, INSN_CLASS_C,   "Cs",        MATCH_C_SRLI64, MASK_C_SRLI64, match_c_slli64, 0 },
-{"c.srai64",   0, INSN_CLASS_C,   "Cs",        MATCH_C_SRAI64, MASK_C_SRAI64, match_c_slli64, 0 },
-{"c.andi",     0, INSN_CLASS_C,   "Cs,Co",     MATCH_C_ANDI, MASK_C_ANDI, match_opcode, 0 },
-{"c.addiw",   64, INSN_CLASS_C,   "d,Co",      MATCH_C_ADDIW, MASK_C_ADDIW, match_rd_nonzero, 0 },
-{"c.addw",    64, INSN_CLASS_C,   "Cs,Ct",     MATCH_C_ADDW, MASK_C_ADDW, match_opcode, 0 },
-{"c.subw",    64, INSN_CLASS_C,   "Cs,Ct",     MATCH_C_SUBW, MASK_C_SUBW, match_opcode, 0 },
-{"c.ldsp",    64, INSN_CLASS_C,   "d,Cn(Cc)",  MATCH_C_LDSP, MASK_C_LDSP, match_rd_nonzero, INSN_DREF|INSN_8_BYTE },
-{"c.ld",      64, INSN_CLASS_C,   "Ct,Cl(Cs)", MATCH_C_LD, MASK_C_LD, match_opcode, INSN_DREF|INSN_8_BYTE },
-{"c.sdsp",    64, INSN_CLASS_C,   "CV,CN(Cc)", MATCH_C_SDSP, MASK_C_SDSP, match_opcode, INSN_DREF|INSN_8_BYTE },
-{"c.sd",      64, INSN_CLASS_C,   "Ct,Cl(Cs)", MATCH_C_SD, MASK_C_SD, match_opcode, INSN_DREF|INSN_8_BYTE },
-{"c.fldsp",    0, INSN_CLASS_D_AND_C, "D,Cn(Cc)",  MATCH_C_FLDSP, MASK_C_FLDSP, match_opcode, INSN_DREF|INSN_8_BYTE },
-{"c.fld",      0, INSN_CLASS_D_AND_C, "CD,Cl(Cs)", MATCH_C_FLD, MASK_C_FLD, match_opcode, INSN_DREF|INSN_8_BYTE },
-{"c.fsdsp",    0, INSN_CLASS_D_AND_C, "CT,CN(Cc)", MATCH_C_FSDSP, MASK_C_FSDSP, match_opcode, INSN_DREF|INSN_8_BYTE },
-{"c.fsd",      0, INSN_CLASS_D_AND_C, "CD,Cl(Cs)", MATCH_C_FSD, MASK_C_FSD, match_opcode, INSN_DREF|INSN_8_BYTE },
-{"c.flwsp",   32, INSN_CLASS_F_AND_C, "D,Cm(Cc)",  MATCH_C_FLWSP, MASK_C_FLWSP, match_opcode, INSN_DREF|INSN_4_BYTE },
-{"c.flw",     32, INSN_CLASS_F_AND_C, "CD,Ck(Cs)", MATCH_C_FLW, MASK_C_FLW, match_opcode, INSN_DREF|INSN_4_BYTE },
-{"c.fswsp",   32, INSN_CLASS_F_AND_C, "CT,CM(Cc)", MATCH_C_FSWSP, MASK_C_FSWSP, match_opcode, INSN_DREF|INSN_4_BYTE },
-{"c.fsw",     32, INSN_CLASS_F_AND_C, "CD,Ck(Cs)", MATCH_C_FSW, MASK_C_FSW, match_opcode, INSN_DREF|INSN_4_BYTE },
+{"c.unimp",    0, INSN_CLASS_ZCA,   "",          0, 0xffffU,  match_opcode, 0 },
+{"c.ebreak",   0, INSN_CLASS_ZCA,   "",          MATCH_C_EBREAK, MASK_C_EBREAK, match_opcode, 0 },
+{"c.jr",       0, INSN_CLASS_ZCA,   "d",         MATCH_C_JR, MASK_C_JR, match_rd_nonzero, INSN_BRANCH },
+{"c.jalr",     0, INSN_CLASS_ZCA,   "d",         MATCH_C_JALR, MASK_C_JALR, match_rd_nonzero, INSN_JSR },
+{"c.j",        0, INSN_CLASS_ZCA,   "Ca",        MATCH_C_J, MASK_C_J, match_opcode, INSN_BRANCH },
+{"c.jal",     32, INSN_CLASS_ZCA,   "Ca",        MATCH_C_JAL, MASK_C_JAL, match_opcode, INSN_JSR },
+{"c.beqz",     0, INSN_CLASS_ZCA,   "Cs,Cp",     MATCH_C_BEQZ, MASK_C_BEQZ, match_opcode, INSN_CONDBRANCH },
+{"c.bnez",     0, INSN_CLASS_ZCA,   "Cs,Cp",     MATCH_C_BNEZ, MASK_C_BNEZ, match_opcode, INSN_CONDBRANCH },
+{"c.lwsp",     0, INSN_CLASS_ZCA,   "d,Cm(Cc)",  MATCH_C_LWSP, MASK_C_LWSP, match_rd_nonzero, 0 },
+{"c.lw",       0, INSN_CLASS_ZCA,   "Ct,Ck(Cs)", MATCH_C_LW, MASK_C_LW, match_opcode, INSN_DREF|INSN_4_BYTE },
+{"c.swsp",     0, INSN_CLASS_ZCA,   "CV,CM(Cc)", MATCH_C_SWSP, MASK_C_SWSP, match_opcode, INSN_DREF|INSN_4_BYTE },
+{"c.sw",       0, INSN_CLASS_ZCA,   "Ct,Ck(Cs)", MATCH_C_SW, MASK_C_SW, match_opcode, INSN_DREF|INSN_4_BYTE },
+{"c.nop",      0, INSN_CLASS_ZCA,   "",          MATCH_C_ADDI, 0xffff, match_opcode, INSN_ALIAS },
+{"c.nop",      0, INSN_CLASS_ZCA,   "Cj",        MATCH_C_ADDI, MASK_C_ADDI|MASK_RD, match_opcode, INSN_ALIAS },
+{"c.mv",       0, INSN_CLASS_ZCA,   "d,CV",      MATCH_C_MV, MASK_C_MV, match_c_add_with_hint, 0 },
+{"c.lui",      0, INSN_CLASS_ZCA,   "d,Cu",      MATCH_C_LUI, MASK_C_LUI, match_c_lui_with_hint, 0 },
+{"c.li",       0, INSN_CLASS_ZCA,   "d,Co",      MATCH_C_LI, MASK_C_LI, match_opcode, 0 },
+{"c.addi4spn", 0, INSN_CLASS_ZCA,   "Ct,Cc,CK",  MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN, match_c_addi4spn, 0 },
+{"c.addi16sp", 0, INSN_CLASS_ZCA,   "Cc,CL",     MATCH_C_ADDI16SP, MASK_C_ADDI16SP, match_c_addi16sp, 0 },
+{"c.addi",     0, INSN_CLASS_ZCA,   "d,Co",      MATCH_C_ADDI, MASK_C_ADDI, match_opcode, 0 },
+{"c.add",      0, INSN_CLASS_ZCA,   "d,CV",      MATCH_C_ADD, MASK_C_ADD, match_c_add_with_hint, 0 },
+{"c.sub",      0, INSN_CLASS_ZCA,   "Cs,Ct",     MATCH_C_SUB, MASK_C_SUB, match_opcode, 0 },
+{"c.and",      0, INSN_CLASS_ZCA,   "Cs,Ct",     MATCH_C_AND, MASK_C_AND, match_opcode, 0 },
+{"c.or",       0, INSN_CLASS_ZCA,   "Cs,Ct",     MATCH_C_OR, MASK_C_OR, match_opcode, 0 },
+{"c.xor",      0, INSN_CLASS_ZCA,   "Cs,Ct",     MATCH_C_XOR, MASK_C_XOR, match_opcode, 0 },
+{"c.slli",     0, INSN_CLASS_ZCA,   "d,C>",      MATCH_C_SLLI, MASK_C_SLLI, match_c_slli, 0 },
+{"c.srli",     0, INSN_CLASS_ZCA,   "Cs,C>",     MATCH_C_SRLI, MASK_C_SRLI, match_c_slli, 0 },
+{"c.srai",     0, INSN_CLASS_ZCA,   "Cs,C>",     MATCH_C_SRAI, MASK_C_SRAI, match_c_slli, 0 },
+{"c.slli64",   0, INSN_CLASS_ZCA,   "d",         MATCH_C_SLLI64, MASK_C_SLLI64, match_c_slli64, 0 },
+{"c.srli64",   0, INSN_CLASS_ZCA,   "Cs",        MATCH_C_SRLI64, MASK_C_SRLI64, match_c_slli64, 0 },
+{"c.srai64",   0, INSN_CLASS_ZCA,   "Cs",        MATCH_C_SRAI64, MASK_C_SRAI64, match_c_slli64, 0 },
+{"c.andi",     0, INSN_CLASS_ZCA,   "Cs,Co",     MATCH_C_ANDI, MASK_C_ANDI, match_opcode, 0 },
+{"c.addiw",   64, INSN_CLASS_ZCA,   "d,Co",      MATCH_C_ADDIW, MASK_C_ADDIW, match_rd_nonzero, 0 },
+{"c.addw",    64, INSN_CLASS_ZCA,   "Cs,Ct",     MATCH_C_ADDW, MASK_C_ADDW, match_opcode, 0 },
+{"c.subw",    64, INSN_CLASS_ZCA,   "Cs,Ct",     MATCH_C_SUBW, MASK_C_SUBW, match_opcode, 0 },
+{"c.ldsp",    64, INSN_CLASS_ZCA,   "d,Cn(Cc)",  MATCH_C_LDSP, MASK_C_LDSP, match_rd_nonzero, INSN_DREF|INSN_8_BYTE },
+{"c.ld",      64, INSN_CLASS_ZCA,   "Ct,Cl(Cs)", MATCH_C_LD, MASK_C_LD, match_opcode, INSN_DREF|INSN_8_BYTE },
+{"c.sdsp",    64, INSN_CLASS_ZCA,   "CV,CN(Cc)", MATCH_C_SDSP, MASK_C_SDSP, match_opcode, INSN_DREF|INSN_8_BYTE },
+{"c.sd",      64, INSN_CLASS_ZCA,   "Ct,Cl(Cs)", MATCH_C_SD, MASK_C_SD, match_opcode, INSN_DREF|INSN_8_BYTE },
+{"c.fldsp",    0, INSN_CLASS_ZCD, "D,Cn(Cc)",  MATCH_C_FLDSP, MASK_C_FLDSP, match_opcode, INSN_DREF|INSN_8_BYTE },
+{"c.fld",      0, INSN_CLASS_ZCD, "CD,Cl(Cs)", MATCH_C_FLD, MASK_C_FLD, match_opcode, INSN_DREF|INSN_8_BYTE },
+{"c.fsdsp",    0, INSN_CLASS_ZCD, "CT,CN(Cc)", MATCH_C_FSDSP, MASK_C_FSDSP, match_opcode, INSN_DREF|INSN_8_BYTE },
+{"c.fsd",      0, INSN_CLASS_ZCD, "CD,Cl(Cs)", MATCH_C_FSD, MASK_C_FSD, match_opcode, INSN_DREF|INSN_8_BYTE },
+{"c.flwsp",   32, INSN_CLASS_ZCF, "D,Cm(Cc)",  MATCH_C_FLWSP, MASK_C_FLWSP, match_opcode, INSN_DREF|INSN_4_BYTE },
+{"c.flw",     32, INSN_CLASS_ZCF, "CD,Ck(Cs)", MATCH_C_FLW, MASK_C_FLW, match_opcode, INSN_DREF|INSN_4_BYTE },
+{"c.fswsp",   32, INSN_CLASS_ZCF, "CT,CM(Cc)", MATCH_C_FSWSP, MASK_C_FSWSP, match_opcode, INSN_DREF|INSN_4_BYTE },
+{"c.fsw",     32, INSN_CLASS_ZCF, "CD,Ck(Cs)", MATCH_C_FSW, MASK_C_FSW, match_opcode, INSN_DREF|INSN_4_BYTE },
 
 /* Zicbom and Zicboz instructions.  */
 {"cbo.clean",  0, INSN_CLASS_ZICBOM, "0(s)", MATCH_CBO_CLEAN, MASK_CBO_CLEAN, match_opcode, 0 },
@@ -3503,39 +3503,39 @@  const struct riscv_opcode riscv_insn_types[] =
 {"j",       0, INSN_CLASS_I,       "O4,d,a",            0, 0, NULL, 0 },
 {"j",       0, INSN_CLASS_F,       "O4,D,a",            0, 0, NULL, 0 },
 
-{"cr",      0, INSN_CLASS_C,       "O2,CF4,d,CV",       0, 0, NULL, 0 },
-{"cr",      0, INSN_CLASS_F_AND_C, "O2,CF4,D,CV",       0, 0, NULL, 0 },
-{"cr",      0, INSN_CLASS_F_AND_C, "O2,CF4,d,CT",       0, 0, NULL, 0 },
-{"cr",      0, INSN_CLASS_F_AND_C, "O2,CF4,D,CT",       0, 0, NULL, 0 },
+{"cr",      0, INSN_CLASS_ZCA,       "O2,CF4,d,CV",       0, 0, NULL, 0 },
+{"cr",      0, INSN_CLASS_ZCF, "O2,CF4,D,CV",       0, 0, NULL, 0 },
+{"cr",      0, INSN_CLASS_ZCF, "O2,CF4,d,CT",       0, 0, NULL, 0 },
+{"cr",      0, INSN_CLASS_ZCF, "O2,CF4,D,CT",       0, 0, NULL, 0 },
 
-{"ci",      0, INSN_CLASS_C,       "O2,CF3,d,Co",       0, 0, NULL, 0 },
-{"ci",      0, INSN_CLASS_F_AND_C, "O2,CF3,D,Co",       0, 0, NULL, 0 },
+{"ci",      0, INSN_CLASS_ZCA,       "O2,CF3,d,Co",       0, 0, NULL, 0 },
+{"ci",      0, INSN_CLASS_ZCF, "O2,CF3,D,Co",       0, 0, NULL, 0 },
 
-{"ciw",     0, INSN_CLASS_C,       "O2,CF3,Ct,C8",      0, 0, NULL, 0 },
-{"ciw",     0, INSN_CLASS_F_AND_C, "O2,CF3,CD,C8",      0, 0, NULL, 0 },
+{"ciw",     0, INSN_CLASS_ZCA,       "O2,CF3,Ct,C8",      0, 0, NULL, 0 },
+{"ciw",     0, INSN_CLASS_ZCF, "O2,CF3,CD,C8",      0, 0, NULL, 0 },
 
-{"css",     0, INSN_CLASS_C,       "O2,CF3,CV,C6",      0, 0, NULL, 0 },
-{"css",     0, INSN_CLASS_F_AND_C, "O2,CF3,CT,C6",      0, 0, NULL, 0 },
+{"css",     0, INSN_CLASS_ZCA,       "O2,CF3,CV,C6",      0, 0, NULL, 0 },
+{"css",     0, INSN_CLASS_ZCF, "O2,CF3,CT,C6",      0, 0, NULL, 0 },
 
-{"cl",      0, INSN_CLASS_C,       "O2,CF3,Ct,C5(Cs)",  0, 0, NULL, 0 },
-{"cl",      0, INSN_CLASS_F_AND_C, "O2,CF3,CD,C5(Cs)",  0, 0, NULL, 0 },
-{"cl",      0, INSN_CLASS_F_AND_C, "O2,CF3,Ct,C5(CS)",  0, 0, NULL, 0 },
-{"cl",      0, INSN_CLASS_F_AND_C, "O2,CF3,CD,C5(CS)",  0, 0, NULL, 0 },
+{"cl",      0, INSN_CLASS_ZCA,       "O2,CF3,Ct,C5(Cs)",  0, 0, NULL, 0 },
+{"cl",      0, INSN_CLASS_ZCF, "O2,CF3,CD,C5(Cs)",  0, 0, NULL, 0 },
+{"cl",      0, INSN_CLASS_ZCF, "O2,CF3,Ct,C5(CS)",  0, 0, NULL, 0 },
+{"cl",      0, INSN_CLASS_ZCF, "O2,CF3,CD,C5(CS)",  0, 0, NULL, 0 },
 
-{"cs",      0, INSN_CLASS_C,       "O2,CF3,Ct,C5(Cs)",  0, 0, NULL, 0 },
-{"cs",      0, INSN_CLASS_F_AND_C, "O2,CF3,CD,C5(Cs)",  0, 0, NULL, 0 },
-{"cs",      0, INSN_CLASS_F_AND_C, "O2,CF3,Ct,C5(CS)",  0, 0, NULL, 0 },
-{"cs",      0, INSN_CLASS_F_AND_C, "O2,CF3,CD,C5(CS)",  0, 0, NULL, 0 },
+{"cs",      0, INSN_CLASS_ZCA,       "O2,CF3,Ct,C5(Cs)",  0, 0, NULL, 0 },
+{"cs",      0, INSN_CLASS_ZCF, "O2,CF3,CD,C5(Cs)",  0, 0, NULL, 0 },
+{"cs",      0, INSN_CLASS_ZCF, "O2,CF3,Ct,C5(CS)",  0, 0, NULL, 0 },
+{"cs",      0, INSN_CLASS_ZCF, "O2,CF3,CD,C5(CS)",  0, 0, NULL, 0 },
 
-{"ca",      0, INSN_CLASS_C,       "O2,CF6,CF2,Cs,Ct",  0, 0, NULL, 0 },
-{"ca",      0, INSN_CLASS_F_AND_C, "O2,CF6,CF2,CS,Ct",  0, 0, NULL, 0 },
-{"ca",      0, INSN_CLASS_F_AND_C, "O2,CF6,CF2,Cs,CD",  0, 0, NULL, 0 },
-{"ca",      0, INSN_CLASS_F_AND_C, "O2,CF6,CF2,CS,CD",  0, 0, NULL, 0 },
+{"ca",      0, INSN_CLASS_ZCA,       "O2,CF6,CF2,Cs,Ct",  0, 0, NULL, 0 },
+{"ca",      0, INSN_CLASS_ZCF, "O2,CF6,CF2,CS,Ct",  0, 0, NULL, 0 },
+{"ca",      0, INSN_CLASS_ZCF, "O2,CF6,CF2,Cs,CD",  0, 0, NULL, 0 },
+{"ca",      0, INSN_CLASS_ZCF, "O2,CF6,CF2,CS,CD",  0, 0, NULL, 0 },
 
-{"cb",      0, INSN_CLASS_C,       "O2,CF3,Cs,Cp",      0, 0, NULL, 0 },
-{"cb",      0, INSN_CLASS_F_AND_C, "O2,CF3,CS,Cp",      0, 0, NULL, 0 },
+{"cb",      0, INSN_CLASS_ZCA,       "O2,CF3,Cs,Cp",      0, 0, NULL, 0 },
+{"cb",      0, INSN_CLASS_ZCF, "O2,CF3,CS,Cp",      0, 0, NULL, 0 },
 
-{"cj",      0, INSN_CLASS_C,       "O2,CF3,Ca",         0, 0, NULL, 0 },
+{"cj",      0, INSN_CLASS_ZCA,       "O2,CF3,Ca",         0, 0, NULL, 0 },
 
 /* Terminate the list.  */
 {0, 0, INSN_CLASS_NONE, 0, 0, 0, 0, 0}