]> 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 0b2021f5cc7903c93c27aced475fdc9ff45fb99d..b34898af8c98c3ee559e9c3126aa70180b653d72 100644 (file)
@@ -1,5 +1,5 @@
 /* RISC-V-specific support for ELF.
-   Copyright (C) 2011-2022 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.  */
 
@@ -599,35 +601,9 @@ 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 */
-        8,                             /* 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 */
-        8,                             /* 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
@@ -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,6 +1104,8 @@ 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},
@@ -1098,6 +1132,7 @@ static struct riscv_implicit_subset riscv_implicit_subsets[] =
   {"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},
@@ -1121,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}
 };
 
@@ -1181,11 +1240,15 @@ 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,           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 },
@@ -1216,6 +1279,21 @@ static struct riscv_supported_ext riscv_supported_std_z_ext[] =
   {"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 },
@@ -1228,15 +1306,22 @@ static struct riscv_supported_ext riscv_supported_std_z_ext[] =
   {"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}
 };
 
@@ -1245,12 +1330,32 @@ static struct riscv_supported_ext riscv_supported_std_zxm_ext[] =
   {NULL, 0, 0, 0, 0}
 };
 
+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}
+};
+
 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_zxm_ext,
+  riscv_supported_vendor_x_ext,
   NULL
 };
 
@@ -1261,7 +1366,7 @@ enum riscv_prefix_ext_class
   RV_ISA_CLASS_S,
   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
@@ -1282,7 +1387,7 @@ static const struct riscv_parse_prefix_config parse_config[] =
   {RV_ISA_CLASS_Z, "z"},
   {RV_ISA_CLASS_S, "s"},
   {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
@@ -1292,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.  */
@@ -1391,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)
@@ -1506,8 +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_X:
-      break;
+    case RV_ISA_CLASS_X: table = riscv_supported_vendor_x_ext; break;
     default:
       table = riscv_supported_std_ext;
     }
@@ -1582,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.
@@ -1640,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.
@@ -1651,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')
@@ -1664,132 +1774,70 @@ riscv_parse_std_ext (riscv_parse_subset_t *rps,
       return NULL;
     }
 
-  while (p != NULL && *p != '\0')
+  while (*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;
-  enum riscv_prefix_ext_class class;
-
-  while (*p)
-    {
-      if (*p == '_')
-       {
-         p++;
-         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';
@@ -1799,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
@@ -1810,13 +1859,22 @@ riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
          return NULL;
        }
 
+      /* Added g as an implicit extension.  */
+      if (class == RV_ISA_CLASS_SINGLE
+         && strcmp (subset, "g") == 0)
+       {
+         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 _"),
@@ -1834,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;
+           }
+       }
     }
 }
 
@@ -1869,13 +1942,6 @@ riscv_parse_check_conflicts (riscv_parse_subset_t *rps)
       rps->error_handler (_("rv%d does not support the `q' extension"), xlen);
       no_conflict = false;
     }
-  if (riscv_lookup_subset (rps->subset_list, "e", &subset)
-      && riscv_lookup_subset (rps->subset_list, "f", &subset))
-    {
-      rps->error_handler
-        (_("rv32e does not support the `f' extension"));
-      no_conflict = false;
-    }
   if (riscv_lookup_subset (rps->subset_list, "zfinx", &subset)
       && riscv_lookup_subset (rps->subset_list, "f", &subset))
     {
@@ -1996,16 +2062,8 @@ riscv_parse_subset (riscv_parse_subset_t *rps,
       return false;
     }
 
-  /* Parsing standard extension.  */
-  p = riscv_parse_std_ext (rps, arch, p);
-
-  if (p == NULL)
-    return false;
-
-  /* Parse prefixed extensions.  */
-  p = riscv_parse_prefixed_ext (rps, arch, p);
-
-  if (p == NULL)
+  /* Parse single standard and prefixed extensions.  */
+  if (riscv_parse_extensions (rps, arch, p) == NULL)
     return false;
 
   /* Finally add implicit extensions according to the current
@@ -2137,6 +2195,7 @@ 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;
 }
 
@@ -2305,6 +2364,8 @@ riscv_multi_subset_supports (riscv_parse_subset_t *rps,
       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:
@@ -2313,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:
@@ -2329,33 +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_OR_ZFINX:
+    case INSN_CLASS_F_INX:
       return (riscv_subset_supports (rps, "f")
              || riscv_subset_supports (rps, "zfinx"));
-    case INSN_CLASS_D_OR_ZDINX:
+    case INSN_CLASS_D_INX:
       return (riscv_subset_supports (rps, "d")
              || riscv_subset_supports (rps, "zdinx"));
-    case INSN_CLASS_Q_OR_ZQINX:
+    case INSN_CLASS_Q_INX:
       return (riscv_subset_supports (rps, "q")
              || riscv_subset_supports (rps, "zqinx"));
-    case INSN_CLASS_ZFH_OR_ZHINX:
+    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_OR_ZHINXMIN:
+    case INSN_CLASS_ZFHMIN_INX:
       return (riscv_subset_supports (rps, "zfhmin")
              || riscv_subset_supports (rps, "zhinxmin"));
-    case INSN_CLASS_ZFHMIN_AND_D:
+    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:
+    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:
@@ -2398,10 +2474,55 @@ riscv_multi_subset_supports (riscv_parse_subset_t *rps,
              || 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_*"));
@@ -2420,6 +2541,14 @@ riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
     {
     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:
@@ -2428,8 +2557,12 @@ riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
       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:
@@ -2454,20 +2587,19 @@ riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
        return "d";
       else
        return "c";
-    case INSN_CLASS_F_OR_ZFINX:
-      /* i18n: Formatted like "extension `f' or `zfinx' required".  */
+    case INSN_CLASS_F_INX:
       return _("f' or `zfinx");
-    case INSN_CLASS_D_OR_ZDINX:
+    case INSN_CLASS_D_INX:
       return _("d' or `zdinx");
-    case INSN_CLASS_Q_OR_ZQINX:
+    case INSN_CLASS_Q_INX:
       return _("q' or `zqinx");
-    case INSN_CLASS_ZFH_OR_ZHINX:
+    case INSN_CLASS_ZFH_INX:
       return _("zfh' or `zhinx");
     case INSN_CLASS_ZFHMIN:
       return "zfhmin";
-    case INSN_CLASS_ZFHMIN_OR_ZHINXMIN:
+    case INSN_CLASS_ZFHMIN_INX:
       return _("zfhmin' or `zhinxmin");
-    case INSN_CLASS_ZFHMIN_AND_D:
+    case INSN_CLASS_ZFHMIN_AND_D_INX:
       if (riscv_subset_supports (rps, "zfhmin"))
        return "d";
       else if (riscv_subset_supports (rps, "d"))
@@ -2478,7 +2610,7 @@ riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
        return "zhinxmin";
       else
        return _("zfhmin' and `d', or `zhinxmin' and `zdinx");
-    case INSN_CLASS_ZFHMIN_AND_Q:
+    case INSN_CLASS_ZFHMIN_AND_Q_INX:
       if (riscv_subset_supports (rps, "zfhmin"))
        return "q";
       else if (riscv_subset_supports (rps, "q"))
@@ -2489,6 +2621,32 @@ riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
        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:
@@ -2523,10 +2681,50 @@ riscv_multi_subset_supports_ext (riscv_parse_subset_t *rps,
       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_*"));