]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - bfd/elfxx-riscv.c
2.41 Release sources
[thirdparty/binutils-gdb.git] / bfd / elfxx-riscv.c
index e2fb4003380d4b3482f2b369fb182ebb69b278a5..b34898af8c98c3ee559e9c3126aa70180b653d72 100644 (file)
@@ -1,5 +1,5 @@
 /* RISC-V-specific support for ELF.
-   Copyright (C) 2011-2021 Free Software Foundation, Inc.
+   Copyright (C) 2011-2023 Free Software Foundation, Inc.
 
    Contributed by Andrew Waterman (andrew@sifive.com).
    Based on TILE-Gx and MIPS targets.
@@ -38,6 +38,8 @@
    relocations for the debug info.  */
 static bfd_reloc_status_type riscv_elf_add_sub_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type riscv_elf_ignore_reloc
+  (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 
 /* The relocation table used for SHT_RELA sections.  */
 
@@ -46,7 +48,7 @@ static reloc_howto_type howto_table[] =
   /* No relocation.  */
   HOWTO (R_RISCV_NONE,                 /* type */
         0,                             /* rightshift */
-        3,                             /* size */
+        0,                             /* size */
         0,                             /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -61,7 +63,7 @@ static reloc_howto_type howto_table[] =
   /* 32 bit relocation.  */
   HOWTO (R_RISCV_32,                   /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -76,7 +78,7 @@ static reloc_howto_type howto_table[] =
   /* 64 bit relocation.  */
   HOWTO (R_RISCV_64,                   /* type */
         0,                             /* rightshift */
-        4,                             /* size */
+        8,                             /* size */
         64,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -91,7 +93,7 @@ static reloc_howto_type howto_table[] =
   /* Relocation against a local symbol in a shared object.  */
   HOWTO (R_RISCV_RELATIVE,             /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -119,7 +121,7 @@ static reloc_howto_type howto_table[] =
 
   HOWTO (R_RISCV_JUMP_SLOT,            /* type */
         0,                             /* rightshift */
-        4,                             /* size */
+        8,                             /* size */
         64,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -134,7 +136,7 @@ static reloc_howto_type howto_table[] =
   /* Dynamic TLS relocations.  */
   HOWTO (R_RISCV_TLS_DTPMOD32,         /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -148,7 +150,7 @@ static reloc_howto_type howto_table[] =
 
   HOWTO (R_RISCV_TLS_DTPMOD64,         /* type */
         0,                             /* rightshift */
-        4,                             /* size */
+        8,                             /* size */
         64,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -162,7 +164,7 @@ static reloc_howto_type howto_table[] =
 
   HOWTO (R_RISCV_TLS_DTPREL32,         /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -176,7 +178,7 @@ static reloc_howto_type howto_table[] =
 
   HOWTO (R_RISCV_TLS_DTPREL64,         /* type */
         0,                             /* rightshift */
-        4,                             /* size */
+        8,                             /* size */
         64,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -190,7 +192,7 @@ static reloc_howto_type howto_table[] =
 
   HOWTO (R_RISCV_TLS_TPREL32,          /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -204,7 +206,7 @@ static reloc_howto_type howto_table[] =
 
   HOWTO (R_RISCV_TLS_TPREL64,          /* type */
         0,                             /* rightshift */
-        4,                             /* size */
+        8,                             /* size */
         64,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -225,7 +227,7 @@ static reloc_howto_type howto_table[] =
   /* 12-bit PC-relative branch offset.  */
   HOWTO (R_RISCV_BRANCH,               /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         true,                          /* pc_relative */
         0,                             /* bitpos */
@@ -240,7 +242,7 @@ static reloc_howto_type howto_table[] =
   /* 20-bit PC-relative jump offset.  */
   HOWTO (R_RISCV_JAL,                  /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         true,                          /* pc_relative */
         0,                             /* bitpos */
@@ -255,7 +257,7 @@ static reloc_howto_type howto_table[] =
   /* 32-bit PC-relative function call (AUIPC/JALR).  */
   HOWTO (R_RISCV_CALL,                 /* type */
         0,                             /* rightshift */
-        4,                             /* size */
+        8,                             /* size */
         64,                            /* bitsize */
         true,                          /* pc_relative */
         0,                             /* bitpos */
@@ -271,7 +273,7 @@ static reloc_howto_type howto_table[] =
   /* Like R_RISCV_CALL, but not locally binding.  */
   HOWTO (R_RISCV_CALL_PLT,             /* type */
         0,                             /* rightshift */
-        4,                             /* size */
+        8,                             /* size */
         64,                            /* bitsize */
         true,                          /* pc_relative */
         0,                             /* bitpos */
@@ -287,7 +289,7 @@ static reloc_howto_type howto_table[] =
   /* High 20 bits of 32-bit PC-relative GOT access.  */
   HOWTO (R_RISCV_GOT_HI20,             /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         true,                          /* pc_relative */
         0,                             /* bitpos */
@@ -302,7 +304,7 @@ static reloc_howto_type howto_table[] =
   /* High 20 bits of 32-bit PC-relative TLS IE GOT access.  */
   HOWTO (R_RISCV_TLS_GOT_HI20,         /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         true,                          /* pc_relative */
         0,                             /* bitpos */
@@ -317,7 +319,7 @@ static reloc_howto_type howto_table[] =
   /* High 20 bits of 32-bit PC-relative TLS GD GOT reference.  */
   HOWTO (R_RISCV_TLS_GD_HI20,          /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         true,                          /* pc_relative */
         0,                             /* bitpos */
@@ -332,7 +334,7 @@ static reloc_howto_type howto_table[] =
   /* High 20 bits of 32-bit PC-relative reference.  */
   HOWTO (R_RISCV_PCREL_HI20,           /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         true,                          /* pc_relative */
         0,                             /* bitpos */
@@ -347,7 +349,7 @@ static reloc_howto_type howto_table[] =
   /* Low 12 bits of a 32-bit PC-relative load or add.  */
   HOWTO (R_RISCV_PCREL_LO12_I,         /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -362,7 +364,7 @@ static reloc_howto_type howto_table[] =
   /* Low 12 bits of a 32-bit PC-relative store.  */
   HOWTO (R_RISCV_PCREL_LO12_S,         /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -377,7 +379,7 @@ static reloc_howto_type howto_table[] =
   /* High 20 bits of 32-bit absolute address.  */
   HOWTO (R_RISCV_HI20,                 /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -392,7 +394,7 @@ static reloc_howto_type howto_table[] =
   /* High 12 bits of 32-bit load or add.  */
   HOWTO (R_RISCV_LO12_I,               /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -407,7 +409,7 @@ static reloc_howto_type howto_table[] =
   /* High 12 bits of 32-bit store.  */
   HOWTO (R_RISCV_LO12_S,               /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -422,7 +424,7 @@ static reloc_howto_type howto_table[] =
   /* High 20 bits of TLS LE thread pointer offset.  */
   HOWTO (R_RISCV_TPREL_HI20,           /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -437,7 +439,7 @@ static reloc_howto_type howto_table[] =
   /* Low 12 bits of TLS LE thread pointer offset for loads and adds.  */
   HOWTO (R_RISCV_TPREL_LO12_I,         /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -452,7 +454,7 @@ static reloc_howto_type howto_table[] =
   /* Low 12 bits of TLS LE thread pointer offset for stores.  */
   HOWTO (R_RISCV_TPREL_LO12_S,         /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -467,7 +469,7 @@ static reloc_howto_type howto_table[] =
   /* TLS LE thread pointer usage.  May be relaxed.  */
   HOWTO (R_RISCV_TPREL_ADD,            /* type */
         0,                             /* rightshift */
-        3,                             /* size */
+        0,                             /* size */
         0,                             /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -482,7 +484,7 @@ static reloc_howto_type howto_table[] =
   /* 8-bit in-place addition, for local label subtraction.  */
   HOWTO (R_RISCV_ADD8,                 /* type */
         0,                             /* rightshift */
-        0,                             /* size */
+        1,                             /* size */
         8,                             /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -497,7 +499,7 @@ static reloc_howto_type howto_table[] =
   /* 16-bit in-place addition, for local label subtraction.  */
   HOWTO (R_RISCV_ADD16,                        /* type */
         0,                             /* rightshift */
-        1,                             /* size */
+        2,                             /* size */
         16,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -512,7 +514,7 @@ static reloc_howto_type howto_table[] =
   /* 32-bit in-place addition, for local label subtraction.  */
   HOWTO (R_RISCV_ADD32,                        /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -527,7 +529,7 @@ static reloc_howto_type howto_table[] =
   /* 64-bit in-place addition, for local label subtraction.  */
   HOWTO (R_RISCV_ADD64,                        /* type */
         0,                             /* rightshift */
-        4,                             /* size */
+        8,                             /* size */
         64,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -542,7 +544,7 @@ static reloc_howto_type howto_table[] =
   /* 8-bit in-place addition, for local label subtraction.  */
   HOWTO (R_RISCV_SUB8,                 /* type */
         0,                             /* rightshift */
-        0,                             /* size */
+        1,                             /* size */
         8,                             /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -557,7 +559,7 @@ static reloc_howto_type howto_table[] =
   /* 16-bit in-place addition, for local label subtraction.  */
   HOWTO (R_RISCV_SUB16,                        /* type */
         0,                             /* rightshift */
-        1,                             /* size */
+        2,                             /* size */
         16,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -572,7 +574,7 @@ static reloc_howto_type howto_table[] =
   /* 32-bit in-place addition, for local label subtraction.  */
   HOWTO (R_RISCV_SUB32,                        /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -587,7 +589,7 @@ static reloc_howto_type howto_table[] =
   /* 64-bit in-place addition, for local label subtraction.  */
   HOWTO (R_RISCV_SUB64,                        /* type */
         0,                             /* rightshift */
-        4,                             /* size */
+        8,                             /* size */
         64,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -599,42 +601,16 @@ static reloc_howto_type howto_table[] =
         MINUS_ONE,                     /* dst_mask */
         false),                        /* pcrel_offset */
 
-  /* GNU extension to record C++ vtable hierarchy */
-  HOWTO (R_RISCV_GNU_VTINHERIT,                /* type */
-        0,                             /* rightshift */
-        4,                             /* size */
-        0,                             /* bitsize */
-        false,                         /* pc_relative */
-        0,                             /* bitpos */
-        complain_overflow_dont,        /* complain_on_overflow */
-        NULL,                          /* special_function */
-        "R_RISCV_GNU_VTINHERIT",       /* name */
-        false,                         /* partial_inplace */
-        0,                             /* src_mask */
-        0,                             /* dst_mask */
-        false),                        /* pcrel_offset */
-
-  /* GNU extension to record C++ vtable member usage */
-  HOWTO (R_RISCV_GNU_VTENTRY,          /* type */
-        0,                             /* rightshift */
-        4,                             /* size */
-        0,                             /* bitsize */
-        false,                         /* pc_relative */
-        0,                             /* bitpos */
-        complain_overflow_dont,        /* complain_on_overflow */
-        _bfd_elf_rel_vtable_reloc_fn,  /* special_function */
-        "R_RISCV_GNU_VTENTRY",         /* name */
-        false,                         /* partial_inplace */
-        0,                             /* src_mask */
-        0,                             /* dst_mask */
-        false),                        /* pcrel_offset */
+  /* 41 and 42 are reserved.  */
+  EMPTY_HOWTO (0),
+  EMPTY_HOWTO (0),
 
   /* Indicates an alignment statement.  The addend field encodes how many
      bytes of NOPs follow the statement.  The desired alignment is the
      addend rounded up to the next power of two.  */
   HOWTO (R_RISCV_ALIGN,                        /* type */
         0,                             /* rightshift */
-        3,                             /* size */
+        0,                             /* size */
         0,                             /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -649,7 +625,7 @@ static reloc_howto_type howto_table[] =
   /* 8-bit PC-relative branch offset.  */
   HOWTO (R_RISCV_RVC_BRANCH,           /* type */
         0,                             /* rightshift */
-        1,                             /* size */
+        2,                             /* size */
         16,                            /* bitsize */
         true,                          /* pc_relative */
         0,                             /* bitpos */
@@ -664,7 +640,7 @@ static reloc_howto_type howto_table[] =
   /* 11-bit PC-relative jump offset.  */
   HOWTO (R_RISCV_RVC_JUMP,             /* type */
         0,                             /* rightshift */
-        1,                             /* size */
+        2,                             /* size */
         16,                            /* bitsize */
         true,                          /* pc_relative */
         0,                             /* bitpos */
@@ -679,7 +655,7 @@ static reloc_howto_type howto_table[] =
   /* High 6 bits of 18-bit absolute address.  */
   HOWTO (R_RISCV_RVC_LUI,              /* type */
         0,                             /* rightshift */
-        1,                             /* size */
+        2,                             /* size */
         16,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -694,7 +670,7 @@ static reloc_howto_type howto_table[] =
   /* GP-relative load.  */
   HOWTO (R_RISCV_GPREL_I,              /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -709,7 +685,7 @@ static reloc_howto_type howto_table[] =
   /* GP-relative store.  */
   HOWTO (R_RISCV_GPREL_S,              /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -724,7 +700,7 @@ static reloc_howto_type howto_table[] =
   /* TP-relative TLS LE load.  */
   HOWTO (R_RISCV_TPREL_I,              /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -739,7 +715,7 @@ static reloc_howto_type howto_table[] =
   /* TP-relative TLS LE store.  */
   HOWTO (R_RISCV_TPREL_S,              /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -754,7 +730,7 @@ static reloc_howto_type howto_table[] =
   /* The paired relocation may be relaxed.  */
   HOWTO (R_RISCV_RELAX,                        /* type */
         0,                             /* rightshift */
-        3,                             /* size */
+        0,                             /* size */
         0,                             /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -769,7 +745,7 @@ static reloc_howto_type howto_table[] =
   /* 6-bit in-place addition, for local label subtraction.  */
   HOWTO (R_RISCV_SUB6,                 /* type */
         0,                             /* rightshift */
-        0,                             /* size */
+        1,                             /* size */
         8,                             /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -784,7 +760,7 @@ static reloc_howto_type howto_table[] =
   /* 6-bit in-place setting, for local label subtraction.  */
   HOWTO (R_RISCV_SET6,                 /* type */
         0,                             /* rightshift */
-        0,                             /* size */
+        1,                             /* size */
         8,                             /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -799,7 +775,7 @@ static reloc_howto_type howto_table[] =
   /* 8-bit in-place setting, for local label subtraction.  */
   HOWTO (R_RISCV_SET8,                 /* type */
         0,                             /* rightshift */
-        0,                             /* size */
+        1,                             /* size */
         8,                             /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -814,7 +790,7 @@ static reloc_howto_type howto_table[] =
   /* 16-bit in-place setting, for local label subtraction.  */
   HOWTO (R_RISCV_SET16,                        /* type */
         0,                             /* rightshift */
-        1,                             /* size */
+        2,                             /* size */
         16,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -829,7 +805,7 @@ static reloc_howto_type howto_table[] =
   /* 32-bit in-place setting, for local label subtraction.  */
   HOWTO (R_RISCV_SET32,                        /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -844,7 +820,7 @@ static reloc_howto_type howto_table[] =
   /* 32-bit PC relative.  */
   HOWTO (R_RISCV_32_PCREL,             /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         true,                          /* pc_relative */
         0,                             /* bitpos */
@@ -859,7 +835,7 @@ static reloc_howto_type howto_table[] =
   /* Relocation against a local ifunc symbol in a shared object.  */
   HOWTO (R_RISCV_IRELATIVE,            /* type */
         0,                             /* rightshift */
-        2,                             /* size */
+        4,                             /* size */
         32,                            /* bitsize */
         false,                         /* pc_relative */
         0,                             /* bitpos */
@@ -870,6 +846,39 @@ static reloc_howto_type howto_table[] =
         0,                             /* src_mask */
         0xffffffff,                    /* dst_mask */
         false),                        /* pcrel_offset */
+
+  /* Reserved for R_RISCV_PLT32.  */
+  EMPTY_HOWTO (59),
+
+  /* N-bit in-place setting, for unsigned-leb128 local label subtraction.  */
+  HOWTO (R_RISCV_SET_ULEB128,          /* type */
+        0,                             /* rightshift */
+        0,                             /* size */
+        0,                             /* bitsize */
+        false,                         /* pc_relative */
+        0,                             /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        riscv_elf_ignore_reloc,        /* special_function */
+        "R_RISCV_SET_ULEB128",         /* name */
+        false,                         /* partial_inplace */
+        0,                             /* src_mask */
+        0,                             /* dst_mask */
+        false),                        /* pcrel_offset */
+
+  /* N-bit in-place addition, for unsigned-leb128 local label subtraction.  */
+  HOWTO (R_RISCV_SUB_ULEB128,          /* type */
+        0,                             /* rightshift */
+        0,                             /* size */
+        0,                             /* bitsize */
+        false,                         /* pc_relative */
+        0,                             /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        riscv_elf_ignore_reloc,        /* special_function */
+        "R_RISCV_SUB_ULEB128",         /* name */
+        false,                         /* partial_inplace */
+        0,                             /* src_mask */
+        0,                             /* dst_mask */
+        false),                        /* pcrel_offset */
 };
 
 /* A mapping from BFD reloc types to RISC-V ELF reloc types.  */
@@ -931,6 +940,8 @@ static const struct elf_reloc_map riscv_reloc_map[] =
   { BFD_RELOC_RISCV_SET16, R_RISCV_SET16 },
   { BFD_RELOC_RISCV_SET32, R_RISCV_SET32 },
   { BFD_RELOC_RISCV_32_PCREL, R_RISCV_32_PCREL },
+  { BFD_RELOC_RISCV_SET_ULEB128, R_RISCV_SET_ULEB128 },
+  { BFD_RELOC_RISCV_SUB_ULEB128, R_RISCV_SUB_ULEB128 },
 };
 
 /* Given a BFD reloc type, return a howto structure.  */
@@ -1020,6 +1031,10 @@ riscv_elf_add_sub_reloc (bfd *abfd,
       relocation = old_value + relocation;
       break;
     case R_RISCV_SUB6:
+      relocation = (old_value & ~howto->dst_mask)
+                  | (((old_value & howto->dst_mask) - relocation)
+                     & howto->dst_mask);
+      break;
     case R_RISCV_SUB8:
     case R_RISCV_SUB16:
     case R_RISCV_SUB32:
@@ -1032,6 +1047,23 @@ riscv_elf_add_sub_reloc (bfd *abfd,
   return bfd_reloc_ok;
 }
 
+/* Special handler for relocations which don't have to be relocated.
+   This function just simply return bfd_reloc_ok.  */
+
+static bfd_reloc_status_type
+riscv_elf_ignore_reloc (bfd *abfd ATTRIBUTE_UNUSED,
+                       arelent *reloc_entry,
+                       asymbol *symbol ATTRIBUTE_UNUSED,
+                       void *data ATTRIBUTE_UNUSED,
+                       asection *input_section,
+                       bfd *output_bfd,
+                       char **error_message ATTRIBUTE_UNUSED)
+{
+  if (output_bfd != NULL)
+    reloc_entry->address += input_section->output_offset;
+  return bfd_reloc_ok;
+}
+
 /* Always add the IMPLICIT for the SUBSET.  */
 
 static bool
@@ -1072,9 +1104,44 @@ static struct riscv_implicit_subset riscv_implicit_subsets[] =
   {"g", "d",           check_implicit_always},
   {"g", "zicsr",       check_implicit_always},
   {"g", "zifencei",    check_implicit_always},
+  {"m", "zmmul",       check_implicit_always},
+  {"h", "zicsr",       check_implicit_always},
   {"q", "d",           check_implicit_always},
+  {"v", "d",           check_implicit_always},
+  {"v", "zve64d",      check_implicit_always},
+  {"v", "zvl128b",     check_implicit_always},
+  {"zve64d", "d",      check_implicit_always},
+  {"zve64d", "zve64f", check_implicit_always},
+  {"zve64f", "zve32f", check_implicit_always},
+  {"zve64f", "zve64x", check_implicit_always},
+  {"zve64f", "zvl64b", check_implicit_always},
+  {"zve32f", "f",      check_implicit_always},
+  {"zve32f", "zvl32b", check_implicit_always},
+  {"zve32f", "zve32x", check_implicit_always},
+  {"zve64x", "zve32x", check_implicit_always},
+  {"zve64x", "zvl64b", check_implicit_always},
+  {"zve32x", "zvl32b", 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},
+  {"zfa", "f",         check_implicit_always},
   {"d", "f",           check_implicit_always},
+  {"zfh", "zfhmin",    check_implicit_always},
+  {"zfhmin", "f",      check_implicit_always},
   {"f", "zicsr",       check_implicit_always},
+  {"zqinx", "zdinx",   check_implicit_always},
+  {"zdinx", "zfinx",   check_implicit_always},
+  {"zhinx", "zhinxmin",        check_implicit_always},
+  {"zhinxmin", "zfinx",        check_implicit_always},
+  {"zfinx", "zicsr",   check_implicit_always},
   {"zk", "zkn",                check_implicit_always},
   {"zk", "zkr",                check_implicit_always},
   {"zk", "zkt",                check_implicit_always},
@@ -1089,6 +1156,30 @@ static struct riscv_implicit_subset riscv_implicit_subsets[] =
   {"zks", "zbkx",      check_implicit_always},
   {"zks", "zksed",     check_implicit_always},
   {"zks", "zksh",      check_implicit_always},
+  {"zvkn", "zvkned",   check_implicit_always},
+  {"zvkn", "zvknha",   check_implicit_always},
+  {"zvkn", "zvknhb",   check_implicit_always},
+  {"zvkn", "zvbb",     check_implicit_always},
+  {"zvkn", "zvkt",     check_implicit_always},
+  {"zvkng", "zvkn",    check_implicit_always},
+  {"zvkng", "zvkg",    check_implicit_always},
+  {"zvknc", "zvkn",    check_implicit_always},
+  {"zvknc", "zvbc",    check_implicit_always},
+  {"zvks", "zvksed",   check_implicit_always},
+  {"zvks", "zvksh",    check_implicit_always},
+  {"zvks", "zvbb",     check_implicit_always},
+  {"zvks", "zvkt",     check_implicit_always},
+  {"zvksg", "zvks",    check_implicit_always},
+  {"zvksg", "zvkg",    check_implicit_always},
+  {"zvksc", "zvks",    check_implicit_always},
+  {"zvksc", "zvbc",    check_implicit_always},
+  {"smaia", "ssaia",           check_implicit_always},
+  {"smstateen", "ssstateen",   check_implicit_always},
+  {"smepmp", "zicsr",          check_implicit_always},
+  {"ssaia", "zicsr",           check_implicit_always},
+  {"sscofpmf", "zicsr",                check_implicit_always},
+  {"ssstateen", "zicsr",       check_implicit_always},
+  {"sstc", "zicsr",            check_implicit_always},
   {NULL, NULL, NULL}
 };
 
@@ -1136,27 +1227,35 @@ static struct riscv_supported_ext riscv_supported_std_ext[] =
   {"q",                ISA_SPEC_CLASS_20191213,        2, 2, 0 },
   {"q",                ISA_SPEC_CLASS_20190608,        2, 2, 0 },
   {"q",                ISA_SPEC_CLASS_2P2,             2, 0, 0 },
-  {"l",                ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 },
   {"c",                ISA_SPEC_CLASS_20191213,        2, 0, 0 },
   {"c",                ISA_SPEC_CLASS_20190608,        2, 0, 0 },
   {"c",                ISA_SPEC_CLASS_2P2,             2, 0, 0 },
-  {"b",                ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 },
-  {"k",                ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 },
-  {"j",                ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 },
-  {"t",                ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 },
-  {"p",                ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 },
-  {"v",                ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 },
-  {"n",                ISA_SPEC_CLASS_NONE, RISCV_UNKNOWN_VERSION, RISCV_UNKNOWN_VERSION, 0 },
+  {"v",                ISA_SPEC_CLASS_DRAFT,           1, 0, 0 },
+  {"h",                ISA_SPEC_CLASS_DRAFT,           1, 0, 0 },
   {NULL, 0, 0, 0, 0}
 };
 
 static struct riscv_supported_ext riscv_supported_std_z_ext[] =
 {
+  {"zicbom",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zicbop",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zicboz",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zicond",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
   {"zicsr",            ISA_SPEC_CLASS_20191213,        2, 0,  0 },
   {"zicsr",            ISA_SPEC_CLASS_20190608,        2, 0,  0 },
   {"zifencei",         ISA_SPEC_CLASS_20191213,        2, 0,  0 },
   {"zifencei",         ISA_SPEC_CLASS_20190608,        2, 0,  0 },
-  {"zihintpause",      ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zihintpause",      ISA_SPEC_CLASS_DRAFT,           2, 0,  0 },
+  {"zmmul",            ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zawrs",            ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zfa",              ISA_SPEC_CLASS_DRAFT,           0, 1,  0 },
+  {"zfh",              ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zfhmin",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zfinx",            ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zdinx",            ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zqinx",            ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zhinx",            ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zhinxmin",         ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
   {"zbb",              ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
   {"zba",              ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
   {"zbc",              ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
@@ -1174,21 +1273,79 @@ static struct riscv_supported_ext riscv_supported_std_z_ext[] =
   {"zksed",            ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
   {"zksh",             ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
   {"zkt",              ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zve32x",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zve32f",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zve32d",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zve64x",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zve64f",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zve64d",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvbb",             ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvbc",             ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvkg",             ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvkn",             ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvkng",            ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvknc",            ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvkned",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvknha",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvknhb",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvksed",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvksh",            ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvks",             ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvksg",            ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvksc",            ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvkt",             ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvl32b",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvl64b",           ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvl128b",          ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvl256b",          ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvl512b",          ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvl1024b",         ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvl2048b",         ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvl4096b",         ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvl8192b",         ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvl16384b",                ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvl32768b",                ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"zvl65536b",                ISA_SPEC_CLASS_DRAFT,           1, 0,  0 },
+  {"ztso",             ISA_SPEC_CLASS_DRAFT,           0, 1,  0 },
   {NULL, 0, 0, 0, 0}
 };
 
 static struct riscv_supported_ext riscv_supported_std_s_ext[] =
 {
+  {"smaia",            ISA_SPEC_CLASS_DRAFT,           1, 0, 0 },
+  {"smepmp",           ISA_SPEC_CLASS_DRAFT,           1, 0, 0 },
+  {"smstateen",                ISA_SPEC_CLASS_DRAFT,           1, 0, 0 },
+  {"ssaia",            ISA_SPEC_CLASS_DRAFT,           1, 0, 0 },
+  {"sscofpmf",         ISA_SPEC_CLASS_DRAFT,           1, 0, 0 },
+  {"ssstateen",                ISA_SPEC_CLASS_DRAFT,           1, 0, 0 },
+  {"sstc",             ISA_SPEC_CLASS_DRAFT,           1, 0, 0 },
+  {"svinval",          ISA_SPEC_CLASS_DRAFT,           1, 0, 0 },
+  {"svnapot",          ISA_SPEC_CLASS_DRAFT,           1, 0, 0 },
+  {"svpbmt",           ISA_SPEC_CLASS_DRAFT,           1, 0, 0 },
   {NULL, 0, 0, 0, 0}
 };
 
-static struct riscv_supported_ext riscv_supported_std_h_ext[] =
+static struct riscv_supported_ext riscv_supported_std_zxm_ext[] =
 {
   {NULL, 0, 0, 0, 0}
 };
 
-static struct riscv_supported_ext riscv_supported_std_zxm_ext[] =
+static struct riscv_supported_ext riscv_supported_vendor_x_ext[] =
 {
+  {"xtheadba",         ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
+  {"xtheadbb",         ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
+  {"xtheadbs",         ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
+  {"xtheadcmo",                ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
+  {"xtheadcondmov",    ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
+  {"xtheadfmemidx",    ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
+  {"xtheadfmv",                ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
+  {"xtheadint",                ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
+  {"xtheadmac",                ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
+  {"xtheadmemidx",     ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
+  {"xtheadmempair",    ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
+  {"xtheadsync",       ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
+  /* XVentanaCondOps: https://github.com/ventanamicro/ventana-custom-extensions/releases/download/v1.0.0/ventana-custom-extensions-v1.0.0.pdf */
+  {"xventanacondops",  ISA_SPEC_CLASS_DRAFT,   1, 0, 0 },
   {NULL, 0, 0, 0, 0}
 };
 
@@ -1197,8 +1354,8 @@ const struct riscv_supported_ext *riscv_all_supported_ext[] =
   riscv_supported_std_ext,
   riscv_supported_std_z_ext,
   riscv_supported_std_s_ext,
-  riscv_supported_std_h_ext,
   riscv_supported_std_zxm_ext,
+  riscv_supported_vendor_x_ext,
   NULL
 };
 
@@ -1207,10 +1364,9 @@ enum riscv_prefix_ext_class
 {
   RV_ISA_CLASS_Z = 1,
   RV_ISA_CLASS_S,
-  RV_ISA_CLASS_H,
   RV_ISA_CLASS_ZXM,
   RV_ISA_CLASS_X,
-  RV_ISA_CLASS_UNKNOWN
+  RV_ISA_CLASS_SINGLE
 };
 
 /* Record the strings of the prefixed extensions, and their corresponding
@@ -1230,9 +1386,8 @@ static const struct riscv_parse_prefix_config parse_config[] =
   {RV_ISA_CLASS_ZXM, "zxm"},
   {RV_ISA_CLASS_Z, "z"},
   {RV_ISA_CLASS_S, "s"},
-  {RV_ISA_CLASS_H, "h"},
   {RV_ISA_CLASS_X, "x"},
-  {RV_ISA_CLASS_UNKNOWN, NULL}
+  {RV_ISA_CLASS_SINGLE, NULL}
 };
 
 /* Get the prefixed name class for the extensions, the class also
@@ -1242,14 +1397,14 @@ static enum riscv_prefix_ext_class
 riscv_get_prefix_class (const char *arch)
 {
   int i = 0;
-  while (parse_config[i].class != RV_ISA_CLASS_UNKNOWN)
+  while (parse_config[i].class != RV_ISA_CLASS_SINGLE)
     {
       if (strncmp (arch, parse_config[i].prefix,
                   strlen (parse_config[i].prefix)) == 0)
        return parse_config[i].class;
       i++;
     }
-  return RV_ISA_CLASS_UNKNOWN;
+  return RV_ISA_CLASS_SINGLE;
 }
 
 /* Check KNOWN_EXTS to see if the EXT is supported.  */
@@ -1280,8 +1435,6 @@ riscv_recognized_prefixed_ext (const char *ext)
     return riscv_known_prefixed_ext (ext, riscv_supported_std_zxm_ext);
   case RV_ISA_CLASS_S:
     return riscv_known_prefixed_ext (ext, riscv_supported_std_s_ext);
-  case RV_ISA_CLASS_H:
-    return riscv_known_prefixed_ext (ext, riscv_supported_std_h_ext);
   case RV_ISA_CLASS_X:
     /* Only the single x is unrecognized.  */
     if (strcmp (ext, "x") != 0)
@@ -1292,6 +1445,9 @@ riscv_recognized_prefixed_ext (const char *ext)
   return false;
 }
 
+/* Canonical order for single letter extensions.  */
+static const char riscv_ext_canonical_order[] = "eigmafdqlcbkjtpvnh";
+
 /* Array is used to compare the orders of standard extensions quickly.  */
 static int riscv_ext_order[26] = {0};
 
@@ -1307,16 +1463,8 @@ riscv_init_ext_order (void)
   /* The orders of all standard extensions are positive.  */
   int order = 1;
 
-  int i = 0;
-  while (riscv_supported_std_ext[i].name != NULL)
-    {
-      const char *ext = riscv_supported_std_ext[i].name;
-      riscv_ext_order[(*ext - 'a')] = order++;
-      i++;
-      while (riscv_supported_std_ext[i].name
-            && strcmp (ext, riscv_supported_std_ext[i].name) == 0)
-       i++;
-    }
+  for (const char *ext = &riscv_ext_canonical_order[0]; *ext; ++ext)
+    riscv_ext_order[(*ext - 'a')] = order++;
 
   /* Some of the prefixed keyword are not single letter, so we set
      their prefixed orders in the riscv_compare_subsets directly,
@@ -1348,9 +1496,9 @@ riscv_compare_subsets (const char *subset1, const char *subset2)
   enum riscv_prefix_ext_class class1 = riscv_get_prefix_class (subset1);
   enum riscv_prefix_ext_class class2 = riscv_get_prefix_class (subset2);
 
-  if (class1 != RV_ISA_CLASS_UNKNOWN)
+  if (class1 != RV_ISA_CLASS_SINGLE)
     order1 = - (int) class1;
-  if (class2 != RV_ISA_CLASS_UNKNOWN)
+  if (class2 != RV_ISA_CLASS_SINGLE)
     order2 = - (int) class2;
 
   if (order1 == order2)
@@ -1463,9 +1611,7 @@ riscv_get_default_ext_version (enum riscv_spec_class *default_isa_spec,
     case RV_ISA_CLASS_ZXM: table = riscv_supported_std_zxm_ext; break;
     case RV_ISA_CLASS_Z: table = riscv_supported_std_z_ext; break;
     case RV_ISA_CLASS_S: table = riscv_supported_std_s_ext; break;
-    case RV_ISA_CLASS_H: table = riscv_supported_std_h_ext; break;
-    case RV_ISA_CLASS_X:
-      break;
+    case RV_ISA_CLASS_X: table = riscv_supported_vendor_x_ext; break;
     default:
       table = riscv_supported_std_ext;
     }
@@ -1513,7 +1659,9 @@ riscv_parse_add_subset (riscv_parse_subset_t *rps,
        rps->error_handler
          (_("x ISA extension `%s' must be set with the versions"),
           subset);
-      else
+      /* Allow old ISA spec can recognize zicsr and zifencei.  */
+      else if (strcmp (subset, "zicsr") != 0
+              && strcmp (subset, "zifencei") != 0)
        rps->error_handler
          (_("cannot find default versions of the ISA extension `%s'"),
           subset);
@@ -1538,6 +1686,12 @@ riscv_release_subset_list (riscv_subset_list_t *subset_list)
     }
 
   subset_list->tail = NULL;
+
+  if (subset_list->arch_str != NULL)
+    {
+      free ((void*) subset_list->arch_str);
+      subset_list->arch_str = NULL;
+    }
 }
 
 /* Parsing extension version.
@@ -1596,7 +1750,7 @@ riscv_parsing_subset_version (const char *p,
   return p;
 }
 
-/* Parsing function for standard extensions.
+/* Parsing function for both standard and prefixed extensions.
 
    Return Value:
      Points to the end of extensions.
@@ -1607,9 +1761,9 @@ riscv_parsing_subset_version (const char *p,
      `p`: Curent parsing position.  */
 
 static const char *
-riscv_parse_std_ext (riscv_parse_subset_t *rps,
-                    const char *arch,
-                    const char *p)
+riscv_parse_extensions (riscv_parse_subset_t *rps,
+                       const char *arch,
+                       const char *p)
 {
   /* First letter must start with i, e or g.  */
   if (*p != 'e' && *p != 'i' && *p != 'g')
@@ -1620,80 +1774,7 @@ riscv_parse_std_ext (riscv_parse_subset_t *rps,
       return NULL;
     }
 
-  while (p != NULL && *p != '\0')
-    {
-      /* Stop when we parsed the known prefix class.  */
-      enum riscv_prefix_ext_class class = riscv_get_prefix_class (p);
-      if (class != RV_ISA_CLASS_UNKNOWN)
-       break;
-
-      if (*p == '_')
-       {
-         p++;
-         continue;
-       }
-
-      bool implicit = false;
-      int major = RISCV_UNKNOWN_VERSION;
-      int minor = RISCV_UNKNOWN_VERSION;
-      char subset[2] = {0, 0};
-
-      subset[0] = *p;
-
-      /* Check if the standard extension is supported.  */
-      if (riscv_ext_order[(subset[0] - 'a')] == 0)
-       {
-         rps->error_handler
-           (_("%s: unknown standard ISA extension `%c'"),
-            arch, subset[0]);
-         return NULL;
-       }
-
-      /* Checking canonical order.  */
-      if (rps->subset_list->tail != NULL
-         && riscv_compare_subsets (rps->subset_list->tail->name, subset) > 0)
-       {
-         rps->error_handler
-           (_("%s: standard ISA extension `%c' is not "
-              "in canonical order"), arch, subset[0]);
-         return NULL;
-       }
-
-      p = riscv_parsing_subset_version (++p, &major, &minor);
-      /* Added g as an implicit extension.  */
-      if (subset[0] == 'g')
-       {
-         implicit = true;
-         major = RISCV_UNKNOWN_VERSION;
-         minor = RISCV_UNKNOWN_VERSION;
-       }
-      riscv_parse_add_subset (rps, subset, major, minor, implicit);
-    }
-
-  return p;
-}
-
-/* Parsing function for prefixed extensions.
-
-   Return Value:
-     Points to the end of extension.
-
-   Arguments:
-     `rps`: Hooks and status for parsing extensions.
-     `arch`: Full ISA string.
-     `p`: Curent parsing position.  */
-
-static const char *
-riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
-                         const char *arch,
-                         const char *p)
-{
-  int major_version;
-  int minor_version;
-  const char *last_name;
-  enum riscv_prefix_ext_class class;
-
-  while (*p)
+  while (*p != '\0')
     {
       if (*p == '_')
        {
@@ -1701,52 +1782,62 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
          continue;
        }
 
-      class = riscv_get_prefix_class (p);
-      if (class == RV_ISA_CLASS_UNKNOWN)
-       {
-         rps->error_handler
-           (_("%s: unknown prefix class for the ISA extension `%s'"),
-            arch, p);
-         return NULL;
-       }
-
       char *subset = xstrdup (p);
-      char *q = subset;
+      char *q = subset;        /* Start of version.  */
       const char *end_of_version;
+      bool implicit = false;
 
-      /* 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)
+      enum riscv_prefix_ext_class class = riscv_get_prefix_class (p);
+      if (class == RV_ISA_CLASS_SINGLE)
        {
-         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 (riscv_ext_order[(*subset - 'a')] == 0)
+           {
+             rps->error_handler
+               (_("%s: unknown standard ISA extension or prefix class `%c'"),
+                arch, *subset);
+             free (subset);
+             return NULL;
+           }
+         q++;
        }
-      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)))
+      else
        {
-         *q = '\0';
-         rps->error_handler
-           (_("%s: invalid prefixed ISA extension `%s' ends with <number>p"),
-            arch, subset);
-         free (subset);
-         return NULL;
+         /* 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)))
+           {
+             *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';
@@ -1756,8 +1847,9 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
          return NULL;
        }
 
-      /* Check that the extension name is well-formed.  */
-      if (rps->check_unknown_prefixed_ext
+      /* 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))
        {
          rps->error_handler
@@ -1767,35 +1859,22 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
          return NULL;
        }
 
-      /* Check that the extension isn't duplicate.  */
-      last_name = rps->subset_list->tail->name;
-      if (!strcasecmp (last_name, subset))
-       {
-         rps->error_handler
-           (_("%s: duplicate prefixed ISA extension `%s'"),
-            arch, subset);
-         free (subset);
-         return NULL;
-       }
-
-      /* Check that the extension is in expected order.  */
-      if (riscv_compare_subsets (last_name, subset) > 0)
+      /* Added g as an implicit extension.  */
+      if (class == RV_ISA_CLASS_SINGLE
+         && strcmp (subset, "g") == 0)
        {
-         rps->error_handler
-           (_("%s: prefixed ISA extension `%s' is not in expected "
-              "order.  It must come before `%s'"),
-            arch, subset, last_name);
-         free (subset);
-         return NULL;
+         implicit = true;
+         major_version = RISCV_UNKNOWN_VERSION;
+         minor_version = RISCV_UNKNOWN_VERSION;
        }
-
       riscv_parse_add_subset (rps, subset,
                              major_version,
-                             minor_version, false);
+                             minor_version, implicit);
       p += end_of_version - subset;
       free (subset);
 
-      if (*p != '\0' && *p != '_')
+      if (class != RV_ISA_CLASS_SINGLE
+         && *p != '\0' && *p != '_')
        {
          rps->error_handler
            (_("%s: prefixed ISA extension must separate with _"),
@@ -1813,14 +1892,29 @@ static void
 riscv_parse_add_implicit_subsets (riscv_parse_subset_t *rps)
 {
   struct riscv_implicit_subset *t = riscv_implicit_subsets;
-  for (; t->subset_name; t++)
+  bool finished = false;
+  while (!finished)
     {
-      riscv_subset_t *subset = NULL;
-      if (riscv_lookup_subset (rps->subset_list, t->subset_name, &subset)
-         && t->check_func (t->implicit_name, subset))
-       riscv_parse_add_subset (rps, t->implicit_name,
-                               RISCV_UNKNOWN_VERSION,
-                               RISCV_UNKNOWN_VERSION, true);
+      finished = true;
+      for (; t->subset_name; t++)
+       {
+         riscv_subset_t *subset = NULL;
+         riscv_subset_t *implicit_subset = NULL;
+         if (riscv_lookup_subset (rps->subset_list, t->subset_name, &subset)
+             && !riscv_lookup_subset (rps->subset_list, t->implicit_name,
+                                      &implicit_subset)
+             && t->check_func (t->implicit_name, subset))
+           {
+             riscv_parse_add_subset (rps, t->implicit_name,
+                                     RISCV_UNKNOWN_VERSION,
+                                     RISCV_UNKNOWN_VERSION, true);
+
+             /* Restart the loop and pick up any new implications.  */
+             finished = false;
+             t = riscv_implicit_subsets;
+             break;
+           }
+       }
     }
 }
 
@@ -1841,19 +1935,42 @@ riscv_parse_check_conflicts (riscv_parse_subset_t *rps)
       no_conflict = false;
     }
   if (riscv_lookup_subset (rps->subset_list, "q", &subset)
+      && (subset->major_version < 2 || (subset->major_version == 2
+                                       && subset->minor_version < 2))
       && xlen < 64)
     {
-      rps->error_handler
-        (_("rv%d does not support the `q' extension"), xlen);
+      rps->error_handler (_("rv%d does not support the `q' extension"), xlen);
       no_conflict = false;
     }
-  if (riscv_lookup_subset (rps->subset_list, "e", &subset)
+  if (riscv_lookup_subset (rps->subset_list, "zfinx", &subset)
       && riscv_lookup_subset (rps->subset_list, "f", &subset))
     {
       rps->error_handler
-        (_("rv32e does not support the `f' extension"));
+       (_("`zfinx' is conflict with the `f/d/q/zfh/zfhmin' extension"));
+      no_conflict = false;
+    }
+
+  bool support_zve = false;
+  bool support_zvl = false;
+  riscv_subset_t *s = rps->subset_list->head;
+  for (; s != NULL; s = s->next)
+    {
+      if (!support_zve
+         && strncmp (s->name, "zve", 3) == 0)
+       support_zve = true;
+      if (!support_zvl
+         && strncmp (s->name, "zvl", 3) == 0)
+       support_zvl = true;
+      if (support_zve && support_zvl)
+       break;
+    }
+  if (support_zvl && !support_zve)
+    {
+      rps->error_handler
+       (_("zvl*b extensions need to enable either `v' or `zve' extension"));
       no_conflict = false;
     }
+
   return no_conflict;
 }
 
@@ -1945,21 +2062,10 @@ riscv_parse_subset (riscv_parse_subset_t *rps,
       return false;
     }
 
-  /* Parsing standard extension.  */
-  p = riscv_parse_std_ext (rps, arch, p);
-
-  if (p == NULL)
+  /* Parse single standard and prefixed extensions.  */
+  if (riscv_parse_extensions (rps, arch, p) == NULL)
     return false;
 
-  /* Parse the different classes of extensions in the specified order.  */
-  while (*p != '\0')
-    {
-      p = riscv_parse_prefixed_ext (rps, arch, p);
-
-      if (p == NULL)
-        return false;
-    }
-
   /* Finally add implicit extensions according to the current
      extensions.  */
   riscv_parse_add_implicit_subsets (rps);
@@ -2061,6 +2167,38 @@ riscv_arch_str (unsigned xlen, const riscv_subset_list_t *subset)
   return attr_str;
 }
 
+/* Copy the subset in the subset list.  */
+
+static struct riscv_subset_t *
+riscv_copy_subset (riscv_subset_list_t *subset_list,
+                  riscv_subset_t *subset)
+{
+  if (subset == NULL)
+    return NULL;
+
+  riscv_subset_t *new = xmalloc (sizeof *new);
+  new->name = xstrdup (subset->name);
+  new->major_version = subset->major_version;
+  new->minor_version = subset->minor_version;
+  new->next = riscv_copy_subset (subset_list, subset->next);
+
+  if (subset->next == NULL)
+    subset_list->tail = new;
+
+  return new;
+}
+
+/* Copy the subset list.  */
+
+riscv_subset_list_t *
+riscv_copy_subset_list (riscv_subset_list_t *subset_list)
+{
+  riscv_subset_list_t *new = xmalloc (sizeof *new);
+  new->head = riscv_copy_subset (new, subset_list->head);
+  new->arch_str = strdup (subset_list->arch_str);
+  return new;
+}
+
 /* Remove the SUBSET from the subset list.  */
 
 static void
@@ -2087,40 +2225,112 @@ riscv_remove_subset (riscv_subset_list_t *subset_list,
 }
 
 /* Add/Remove an extension to/from the subset list.  This is used for
-   the .option rvc or norvc.  */
+   the .option rvc or norvc, and .option arch directives.  */
 
 bool
 riscv_update_subset (riscv_parse_subset_t *rps,
-                    const char *subset,
-                    bool removed)
+                    const char *str)
 {
-  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)))
-    {
-      rps->error_handler
-       (_("riscv_update_subset: unknown ISA extension `%s'"), subset);
-      return false;
-    }
+  const char *p = str;
 
-  if (removed)
+  do
     {
-      if (strcmp (subset, "i") == 0)
+      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;
+       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)))
+       {
+         *q = '\0';
+         rps->error_handler
+           (_("invalid ISA extension ends with <number>p "
+              "in .option arch `%s'"), str);
+         free (subset);
+         return false;
+       }
+
+      end_of_version =
+       riscv_parsing_subset_version (q, &major_version, &minor_version);
+      *q = '\0';
+      if (end_of_version == NULL)
+       {
+         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)))
        {
          rps->error_handler
-           (_("riscv_update_subset: cannot remove extension i from "
-              "the subset list"));
+           (_("unknown ISA extension `%s' in .option arch `%s'"),
+            subset, str);
+         free (subset);
+         return false;
+       }
+
+      if (strcmp (subset, "i") == 0
+         || strcmp (subset, "e") == 0
+         || strcmp (subset, "g") == 0)
+       {
+         rps->error_handler
+           (_("cannot + or - base extension `%s' in .option "
+              "arch `%s'"), subset, str);
+         free (subset);
          return false;
        }
-      riscv_remove_subset (rps->subset_list, subset);
+
+      if (removed)
+       riscv_remove_subset (rps->subset_list, subset);
+      else
+       riscv_parse_add_subset (rps, subset, major_version, minor_version, true);
+      p += end_of_version - subset;
+      free (subset);
     }
-  else
-    riscv_parse_add_subset (rps, subset,
-                           RISCV_UNKNOWN_VERSION,
-                           RISCV_UNKNOWN_VERSION, true);
+  while (*p++ == ',');
 
   riscv_parse_add_implicit_subsets (rps);
   return riscv_parse_check_conflicts (rps);
@@ -2148,6 +2358,14 @@ riscv_multi_subset_supports (riscv_parse_subset_t *rps,
     {
     case INSN_CLASS_I:
       return riscv_subset_supports (rps, "i");
+    case INSN_CLASS_ZICBOM:
+      return riscv_subset_supports (rps, "zicbom");
+    case INSN_CLASS_ZICBOP:
+      return riscv_subset_supports (rps, "zicbop");
+    case INSN_CLASS_ZICBOZ:
+      return riscv_subset_supports (rps, "zicboz");
+    case INSN_CLASS_ZICOND:
+      return riscv_subset_supports (rps, "zicond");
     case INSN_CLASS_ZICSR:
       return riscv_subset_supports (rps, "zicsr");
     case INSN_CLASS_ZIFENCEI:
@@ -2156,8 +2374,12 @@ riscv_multi_subset_supports (riscv_parse_subset_t *rps,
       return riscv_subset_supports (rps, "zihintpause");
     case INSN_CLASS_M:
       return riscv_subset_supports (rps, "m");
+    case INSN_CLASS_ZMMUL:
+      return riscv_subset_supports (rps, "zmmul");
     case INSN_CLASS_A:
       return riscv_subset_supports (rps, "a");
+    case INSN_CLASS_ZAWRS:
+      return riscv_subset_supports (rps, "zawrs");
     case INSN_CLASS_F:
       return riscv_subset_supports (rps, "f");
     case INSN_CLASS_D:
@@ -2172,6 +2394,44 @@ riscv_multi_subset_supports (riscv_parse_subset_t *rps,
     case INSN_CLASS_D_AND_C:
       return (riscv_subset_supports (rps, "d")
              && riscv_subset_supports (rps, "c"));
+    case INSN_CLASS_F_INX:
+      return (riscv_subset_supports (rps, "f")
+             || riscv_subset_supports (rps, "zfinx"));
+    case INSN_CLASS_D_INX:
+      return (riscv_subset_supports (rps, "d")
+             || riscv_subset_supports (rps, "zdinx"));
+    case INSN_CLASS_Q_INX:
+      return (riscv_subset_supports (rps, "q")
+             || riscv_subset_supports (rps, "zqinx"));
+    case INSN_CLASS_ZFH_INX:
+      return (riscv_subset_supports (rps, "zfh")
+             || riscv_subset_supports (rps, "zhinx"));
+    case INSN_CLASS_ZFHMIN:
+      return riscv_subset_supports (rps, "zfhmin");
+    case INSN_CLASS_ZFHMIN_INX:
+      return (riscv_subset_supports (rps, "zfhmin")
+             || riscv_subset_supports (rps, "zhinxmin"));
+    case INSN_CLASS_ZFHMIN_AND_D_INX:
+      return ((riscv_subset_supports (rps, "zfhmin")
+              && riscv_subset_supports (rps, "d"))
+             || (riscv_subset_supports (rps, "zhinxmin")
+                 && riscv_subset_supports (rps, "zdinx")));
+    case INSN_CLASS_ZFHMIN_AND_Q_INX:
+      return ((riscv_subset_supports (rps, "zfhmin")
+              && riscv_subset_supports (rps, "q"))
+             || (riscv_subset_supports (rps, "zhinxmin")
+                 && riscv_subset_supports (rps, "zqinx")));
+    case INSN_CLASS_ZFA:
+      return riscv_subset_supports (rps, "zfa");
+    case INSN_CLASS_D_AND_ZFA:
+      return riscv_subset_supports (rps, "d")
+            && riscv_subset_supports (rps, "zfa");
+    case INSN_CLASS_Q_AND_ZFA:
+      return riscv_subset_supports (rps, "q")
+            && riscv_subset_supports (rps, "zfa");
+    case INSN_CLASS_ZFH_AND_ZFA:
+      return riscv_subset_supports (rps, "zfh")
+            && riscv_subset_supports (rps, "zfa");
     case INSN_CLASS_ZBA:
       return riscv_subset_supports (rps, "zba");
     case INSN_CLASS_ZBB:
@@ -2205,9 +2465,269 @@ riscv_multi_subset_supports (riscv_parse_subset_t *rps,
       return riscv_subset_supports (rps, "zksed");
     case INSN_CLASS_ZKSH:
       return riscv_subset_supports (rps, "zksh");
+    case INSN_CLASS_V:
+      return (riscv_subset_supports (rps, "v")
+             || riscv_subset_supports (rps, "zve64x")
+             || riscv_subset_supports (rps, "zve32x"));
+    case INSN_CLASS_ZVEF:
+      return (riscv_subset_supports (rps, "v")
+             || riscv_subset_supports (rps, "zve64d")
+             || riscv_subset_supports (rps, "zve64f")
+             || riscv_subset_supports (rps, "zve32f"));
+    case INSN_CLASS_ZVBB:
+      return riscv_subset_supports (rps, "zvbb");
+    case INSN_CLASS_ZVBC:
+      return riscv_subset_supports (rps, "zvbc");
+    case INSN_CLASS_ZVKG:
+      return riscv_subset_supports (rps, "zvkg");
+    case INSN_CLASS_ZVKNED:
+      return riscv_subset_supports (rps, "zvkned");
+    case INSN_CLASS_ZVKNHA:
+      return riscv_subset_supports (rps, "zvknha");
+    case INSN_CLASS_ZVKNHB:
+      return riscv_subset_supports (rps, "zvknhb");
+    case INSN_CLASS_ZVKNHA_OR_ZVKNHB:
+      return (riscv_subset_supports (rps, "zvknha")
+             || riscv_subset_supports (rps, "zvknhb"));
+    case INSN_CLASS_ZVKSED:
+      return riscv_subset_supports (rps, "zvksed");
+    case INSN_CLASS_ZVKSH:
+      return riscv_subset_supports (rps, "zvksh");
+    case INSN_CLASS_SVINVAL:
+      return riscv_subset_supports (rps, "svinval");
+    case INSN_CLASS_H:
+      return riscv_subset_supports (rps, "h");
+    case INSN_CLASS_XTHEADBA:
+      return riscv_subset_supports (rps, "xtheadba");
+    case INSN_CLASS_XTHEADBB:
+      return riscv_subset_supports (rps, "xtheadbb");
+    case INSN_CLASS_XTHEADBS:
+      return riscv_subset_supports (rps, "xtheadbs");
+    case INSN_CLASS_XTHEADCMO:
+      return riscv_subset_supports (rps, "xtheadcmo");
+    case INSN_CLASS_XTHEADCONDMOV:
+      return riscv_subset_supports (rps, "xtheadcondmov");
+    case INSN_CLASS_XTHEADFMEMIDX:
+      return riscv_subset_supports (rps, "xtheadfmemidx");
+    case INSN_CLASS_XTHEADFMV:
+      return riscv_subset_supports (rps, "xtheadfmv");
+    case INSN_CLASS_XTHEADINT:
+      return riscv_subset_supports (rps, "xtheadint");
+    case INSN_CLASS_XTHEADMAC:
+      return riscv_subset_supports (rps, "xtheadmac");
+    case INSN_CLASS_XTHEADMEMIDX:
+      return riscv_subset_supports (rps, "xtheadmemidx");
+    case INSN_CLASS_XTHEADMEMPAIR:
+      return riscv_subset_supports (rps, "xtheadmempair");
+    case INSN_CLASS_XTHEADSYNC:
+      return riscv_subset_supports (rps, "xtheadsync");
+    case INSN_CLASS_XVENTANACONDOPS:
+      return riscv_subset_supports (rps, "xventanacondops");
     default:
       rps->error_handler
         (_("internal: unreachable INSN_CLASS_*"));
       return false;
     }
 }
+
+/* Each instuction is belonged to an instruction class INSN_CLASS_*.
+   Call riscv_subset_supports_ext to determine the missing extension.  */
+
+const char *
+riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
+                                enum riscv_insn_class insn_class)
+{
+  switch (insn_class)
+    {
+    case INSN_CLASS_I:
+      return "i";
+    case INSN_CLASS_ZICBOM:
+      return "zicbom";
+    case INSN_CLASS_ZICBOP:
+      return "zicbop";
+    case INSN_CLASS_ZICBOZ:
+      return "zicboz";
+    case INSN_CLASS_ZICOND:
+      return "zicond";
+    case INSN_CLASS_ZICSR:
+      return "zicsr";
+    case INSN_CLASS_ZIFENCEI:
+      return "zifencei";
+    case INSN_CLASS_ZIHINTPAUSE:
+      return "zihintpause";
+    case INSN_CLASS_M:
+      return "m";
+    case INSN_CLASS_ZMMUL:
+      return _ ("m' or `zmmul");
+    case INSN_CLASS_A:
+      return "a";
+    case INSN_CLASS_ZAWRS:
+      return "zawrs";
+    case INSN_CLASS_F:
+      return "f";
+    case INSN_CLASS_D:
+      return "d";
+    case INSN_CLASS_Q:
+      return "q";
+    case INSN_CLASS_C:
+      return "c";
+    case INSN_CLASS_F_AND_C:
+      if (!riscv_subset_supports (rps, "f")
+         && !riscv_subset_supports (rps, "c"))
+       return _("f' and `c");
+      else if (!riscv_subset_supports (rps, "f"))
+       return "f";
+      else
+       return "c";
+    case INSN_CLASS_D_AND_C:
+      if (!riscv_subset_supports (rps, "d")
+         && !riscv_subset_supports (rps, "c"))
+       return _("d' and `c");
+      else if (!riscv_subset_supports (rps, "d"))
+       return "d";
+      else
+       return "c";
+    case INSN_CLASS_F_INX:
+      return _("f' or `zfinx");
+    case INSN_CLASS_D_INX:
+      return _("d' or `zdinx");
+    case INSN_CLASS_Q_INX:
+      return _("q' or `zqinx");
+    case INSN_CLASS_ZFH_INX:
+      return _("zfh' or `zhinx");
+    case INSN_CLASS_ZFHMIN:
+      return "zfhmin";
+    case INSN_CLASS_ZFHMIN_INX:
+      return _("zfhmin' or `zhinxmin");
+    case INSN_CLASS_ZFHMIN_AND_D_INX:
+      if (riscv_subset_supports (rps, "zfhmin"))
+       return "d";
+      else if (riscv_subset_supports (rps, "d"))
+       return "zfhmin";
+      else if (riscv_subset_supports (rps, "zhinxmin"))
+       return "zdinx";
+      else if (riscv_subset_supports (rps, "zdinx"))
+       return "zhinxmin";
+      else
+       return _("zfhmin' and `d', or `zhinxmin' and `zdinx");
+    case INSN_CLASS_ZFHMIN_AND_Q_INX:
+      if (riscv_subset_supports (rps, "zfhmin"))
+       return "q";
+      else if (riscv_subset_supports (rps, "q"))
+       return "zfhmin";
+      else if (riscv_subset_supports (rps, "zhinxmin"))
+       return "zqinx";
+      else if (riscv_subset_supports (rps, "zqinx"))
+       return "zhinxmin";
+      else
+       return _("zfhmin' and `q', or `zhinxmin' and `zqinx");
+    case INSN_CLASS_ZFA:
+      return "zfa";
+    case INSN_CLASS_D_AND_ZFA:
+      if (!riscv_subset_supports (rps, "d")
+         && !riscv_subset_supports (rps, "zfa"))
+       return _("d' and `zfa");
+      else if (!riscv_subset_supports (rps, "d"))
+       return "d";
+      else
+       return "zfa";
+    case INSN_CLASS_Q_AND_ZFA:
+      if (!riscv_subset_supports (rps, "q")
+         && !riscv_subset_supports (rps, "zfa"))
+       return _("q' and `zfa");
+      else if (!riscv_subset_supports (rps, "q"))
+       return "q";
+      else
+       return "zfa";
+    case INSN_CLASS_ZFH_AND_ZFA:
+      if (!riscv_subset_supports (rps, "zfh")
+         && !riscv_subset_supports (rps, "zfa"))
+       return _("zfh' and `zfa");
+      else if (!riscv_subset_supports (rps, "zfh"))
+       return "zfh";
+      else
+       return "zfa";
+    case INSN_CLASS_ZBA:
+      return "zba";
+    case INSN_CLASS_ZBB:
+      return "zbb";
+    case INSN_CLASS_ZBC:
+      return "zbc";
+    case INSN_CLASS_ZBS:
+      return "zbs";
+    case INSN_CLASS_ZBKB:
+      return "zbkb";
+    case INSN_CLASS_ZBKC:
+      return "zbkc";
+    case INSN_CLASS_ZBKX:
+      return "zbkx";
+    case INSN_CLASS_ZBB_OR_ZBKB:
+      return _("zbb' or `zbkb");
+    case INSN_CLASS_ZBC_OR_ZBKC:
+      return _("zbc' or `zbkc");
+    case INSN_CLASS_ZKND:
+      return "zknd";
+    case INSN_CLASS_ZKNE:
+      return "zkne";
+    case INSN_CLASS_ZKNH:
+      return "zknh";
+    case INSN_CLASS_ZKND_OR_ZKNE:
+      return _("zknd' or `zkne");
+    case INSN_CLASS_ZKSED:
+      return "zksed";
+    case INSN_CLASS_ZKSH:
+      return "zksh";
+    case INSN_CLASS_V:
+      return _("v' or `zve64x' or `zve32x");
+    case INSN_CLASS_ZVEF:
+      return _("v' or `zve64d' or `zve64f' or `zve32f");
+    case INSN_CLASS_ZVBB:
+      return _("zvbb");
+    case INSN_CLASS_ZVBC:
+      return _("zvbc");
+    case INSN_CLASS_ZVKG:
+      return _("zvkg");
+    case INSN_CLASS_ZVKNED:
+      return _("zvkned");
+    case INSN_CLASS_ZVKNHA:
+      return _("zvknha");
+    case INSN_CLASS_ZVKNHB:
+      return _("zvknhb");
+    case INSN_CLASS_ZVKSED:
+      return _("zvksed");
+    case INSN_CLASS_ZVKSH:
+      return _("zvksh");
+    case INSN_CLASS_SVINVAL:
+      return "svinval";
+    case INSN_CLASS_H:
+      return _("h");
+    case INSN_CLASS_XTHEADBA:
+      return "xtheadba";
+    case INSN_CLASS_XTHEADBB:
+      return "xtheadbb";
+    case INSN_CLASS_XTHEADBS:
+      return "xtheadbs";
+    case INSN_CLASS_XTHEADCMO:
+      return "xtheadcmo";
+    case INSN_CLASS_XTHEADCONDMOV:
+      return "xtheadcondmov";
+    case INSN_CLASS_XTHEADFMEMIDX:
+      return "xtheadfmemidx";
+    case INSN_CLASS_XTHEADFMV:
+      return "xtheadfmv";
+    case INSN_CLASS_XTHEADINT:
+      return "xtheadint";
+    case INSN_CLASS_XTHEADMAC:
+      return "xtheadmac";
+    case INSN_CLASS_XTHEADMEMIDX:
+      return "xtheadmemidx";
+    case INSN_CLASS_XTHEADMEMPAIR:
+      return "xtheadmempair";
+    case INSN_CLASS_XTHEADSYNC:
+      return "xtheadsync";
+    default:
+      rps->error_handler
+        (_("internal: unreachable INSN_CLASS_*"));
+      return NULL;
+    }
+}