RISC-V: Support to parse the multi-letter prefix in the architecture string.

Message ID 1617787516-29908-1-git-send-email-nelson.chu@sifive.com
State New
Headers show
Series
  • RISC-V: Support to parse the multi-letter prefix in the architecture string.
Related show

Commit Message

Nelson Chu April 7, 2021, 9:25 a.m.
The original discussion is as follows,
https://github.com/riscv/riscv-isa-manual/issues/637

I never considered the prefixes may have multiple letters, like zxm.
But the ISA spec has been updated for a long time that I haven't noticed.
This patch rewrites the part of architecture parser to support parsing
the multi-letter prefixes.  Besides, I also improve the parser to report
errors in details.  One of the most obvious improvement is - Do not parse
the prefixed extensions according to the orders in the parse_config.
If we do so, then we used to get "unexpected ISA string at end" errors,
but the message is a little bit  hard to know what is happening.  I Remove
the confused message, and let riscv_parse_prefixed_ext to report the details.

bfd/
    * elfxx-riscv.c (riscv_std_z_ext_strtab): Moved forward.
    (riscv_std_s_ext_strtab): Likewise.
    (riscv_std_h_ext_strtab): Likewise.
    (riscv_std_zxm_ext_strtab): Added for the zxm prefix.
    (enum riscv_prefix_ext_class): Moved forward and renamed from
    riscv_isa_ext_class.  Reorder them according to the parsing order,
    since the enum values are used to check the orders in the
    riscv_compare_subsets.
    (struct riscv_parse_prefix_config): Moved forward and renamed from
    riscv_parse_config_t.  Also removed the ext_valid_p field, the
    related functions are replaced by riscv_valid_prefixed_ext.
    (parse_config): Moved forward and updated.  The more letters of the
    prefix string, the more forward it must be defined.  Otherwise, we
    will get the wrong mapping when using strncmp in riscv_get_prefix_class.
    (riscv_get_prefix_class): Moved forward.  Support to parse the
    multi-letter prefix, like zxm.
    (riscv_known_prefixed_ext): New function, check if the prefixed
    extension is supported according to the right riscv_std_*_ext_strtab.
    (riscv_valid_prefixed_ext): New function, used to replace the
    riscv_ext_*_valid_p functions.
    (riscv_init_ext_order): Do not set the values for prefix keywords
    since they may have multiple letters for now.
    (riscv_compare_subsets): Set the order values of prefix keywords
    to negative numbers according to the riscv_prefix_ext_class.
    (riscv_parse_std_ext): Call riscv_get_prefix_class to see if we
    have parsed the prefixed extensions.
    (riscv_parse_prefixed_ext): Updated and removed the parameter config.
    Report error when the prefix is unknown.
    (riscv_parse_subset): Do not parse the prefixed extensions according
    to the orders in the parse_config.  Remove the confused message and
    let riscv_parse_prefixed_ext to report the details.
    * elfxx-riscv.h (enum riscv_isa_ext_class): Moved to elfxx-riscv.c.
    (riscv_get_prefix_class): Removed to static.
gas/
    * testsuite/gas/riscv/march-fail-order-x-std.d: Renamed from
    march-fail-porder-x-std.d.
    * testsuite/gas/riscv/march-fail-order-z-std.d: Renamed from
    march-fail-porder-z-std.d.
    * testsuite/gas/riscv/march-fail-order-x-z.d: Renamed from
    march-fail-porder-x-z.d.
    * testsuite/gas/riscv/march-fail-order-zx-std.l: Added to replace
    march-fail-porder.l.
    * testsuite/gas/riscv/march-fail-order-x-z.l: Likewise.
    * testsuite/gas/riscv/march-fail-order-x.l: Updated.
    * testsuite/gas/riscv/march-fail-order-z.l: Likewise.
    * testsuite/gas/riscv/march-fail-single-prefix-h.d: Renamed from
    march-fail-single-char-h.d.
    * testsuite/gas/riscv/march-fail-single-prefix-s.d: Renamed from
    march-fail-single-char-s.d.
    * testsuite/gas/riscv/march-fail-single-prefix-x.d: Renamed from
    march-fail-single-char-x.d.
    * testsuite/gas/riscv/march-fail-single-prefix-z.d: Renamed from
    march-fail-single-char-z.d.
    * testsuite/gas/riscv/march-fail-single-prefix-zmx.d: Added.
    * testsuite/gas/riscv/march-fail-single-prefix.l: Added to replace
    march-fail-single-prefix.l.
    * testsuite/gas/riscv/march-fail-unknown-zxm.d: Added.
    * testsuite/gas/riscv/march-fail-unknown-std.l: Updated.
    * testsuite/gas/riscv/march-fail-unknown.l: Likewise.
---
 bfd/elfxx-riscv.c                                  | 395 ++++++++++-----------
 bfd/elfxx-riscv.h                                  |  13 -
 gas/testsuite/gas/riscv/march-fail-order-x-std.d   |   3 +
 gas/testsuite/gas/riscv/march-fail-order-x-z.d     |   3 +
 gas/testsuite/gas/riscv/march-fail-order-x-z.l     |   2 +
 gas/testsuite/gas/riscv/march-fail-order-x.l       |   2 +-
 gas/testsuite/gas/riscv/march-fail-order-z-std.d   |   3 +
 gas/testsuite/gas/riscv/march-fail-order-z.l       |   2 +-
 gas/testsuite/gas/riscv/march-fail-order-zx-std.l  |   2 +
 gas/testsuite/gas/riscv/march-fail-porder-x-std.d  |   3 -
 gas/testsuite/gas/riscv/march-fail-porder-x-z.d    |   3 -
 gas/testsuite/gas/riscv/march-fail-porder-z-std.d  |   3 -
 gas/testsuite/gas/riscv/march-fail-porder.l        |   2 -
 gas/testsuite/gas/riscv/march-fail-single-char-h.d |   3 -
 gas/testsuite/gas/riscv/march-fail-single-char-s.d |   3 -
 gas/testsuite/gas/riscv/march-fail-single-char-x.d |   3 -
 gas/testsuite/gas/riscv/march-fail-single-char-z.d |   3 -
 gas/testsuite/gas/riscv/march-fail-single-char.l   |   2 -
 .../gas/riscv/march-fail-single-prefix-h.d         |   3 +
 .../gas/riscv/march-fail-single-prefix-s.d         |   3 +
 .../gas/riscv/march-fail-single-prefix-x.d         |   3 +
 .../gas/riscv/march-fail-single-prefix-z.d         |   3 +
 .../gas/riscv/march-fail-single-prefix-zxm.d       |   3 +
 gas/testsuite/gas/riscv/march-fail-single-prefix.l |   2 +
 gas/testsuite/gas/riscv/march-fail-unknown-std.l   |   2 +-
 gas/testsuite/gas/riscv/march-fail-unknown-zxm.d   |   3 +
 gas/testsuite/gas/riscv/march-fail-unknown.l       |   2 +-
 27 files changed, 233 insertions(+), 241 deletions(-)
 create mode 100644 gas/testsuite/gas/riscv/march-fail-order-x-std.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-order-x-z.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-order-x-z.l
 create mode 100644 gas/testsuite/gas/riscv/march-fail-order-z-std.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-order-zx-std.l
 delete mode 100644 gas/testsuite/gas/riscv/march-fail-porder-x-std.d
 delete mode 100644 gas/testsuite/gas/riscv/march-fail-porder-x-z.d
 delete mode 100644 gas/testsuite/gas/riscv/march-fail-porder-z-std.d
 delete mode 100644 gas/testsuite/gas/riscv/march-fail-porder.l
 delete mode 100644 gas/testsuite/gas/riscv/march-fail-single-char-h.d
 delete mode 100644 gas/testsuite/gas/riscv/march-fail-single-char-s.d
 delete mode 100644 gas/testsuite/gas/riscv/march-fail-single-char-x.d
 delete mode 100644 gas/testsuite/gas/riscv/march-fail-single-char-z.d
 delete mode 100644 gas/testsuite/gas/riscv/march-fail-single-char.l
 create mode 100644 gas/testsuite/gas/riscv/march-fail-single-prefix-h.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-single-prefix-s.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-single-prefix-x.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-single-prefix-z.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-single-prefix-zxm.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-single-prefix.l
 create mode 100644 gas/testsuite/gas/riscv/march-fail-unknown-zxm.d

-- 
2.7.4

Comments

Jim Wilson April 8, 2021, 6:11 p.m. | #1
On Wed, Apr 7, 2021 at 2:25 AM Nelson Chu <nelson.chu@sifive.com> wrote:

> I never considered the prefixes may have multiple letters, like zxm.

> But the ISA spec has been updated for a long time that I haven't noticed.

> This patch rewrites the part of architecture parser to support parsing

> the multi-letter prefixes.  Besides, I also improve the parser to report

> errors in details.  One of the most obvious improvement is - Do not parse

> the prefixed extensions according to the orders in the parse_config.

> If we do so, then we used to get "unexpected ISA string at end" errors,

> but the message is a little bit  hard to know what is happening.  I Remove

> the confused message, and let riscv_parse_prefixed_ext to report the

> details.

>


Looks reasonable.  We will need a similar patch for gcc, but we don't need
it now.

Jim
Nelson Chu April 12, 2021, 2:10 a.m. | #2
On Fri, Apr 9, 2021 at 2:11 AM Jim Wilson <jimw@sifive.com> wrote:
>

> On Wed, Apr 7, 2021 at 2:25 AM Nelson Chu <nelson.chu@sifive.com> wrote:

>>

>> I never considered the prefixes may have multiple letters, like zxm.

>> But the ISA spec has been updated for a long time that I haven't noticed.

>> This patch rewrites the part of architecture parser to support parsing

>> the multi-letter prefixes.  Besides, I also improve the parser to report

>> errors in details.  One of the most obvious improvement is - Do not parse

>> the prefixed extensions according to the orders in the parse_config.

>> If we do so, then we used to get "unexpected ISA string at end" errors,

>> but the message is a little bit  hard to know what is happening.  I Remove

>> the confused message, and let riscv_parse_prefixed_ext to report the details.

>

>

> Looks reasonable.  We will need a similar patch for gcc, but we don't need it now.


Committed, thanks.

Patch

diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index f6a2509..0d8c7da 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -1028,16 +1028,167 @@  riscv_elf_add_sub_reloc (bfd *abfd,
 
 #define RISCV_UNKNOWN_VERSION -1
 
-/* Array is used to compare the orders of all extensions quickly.
+/* Lists of prefixed class extensions that binutils should know about.
+   Whether or not a particular entry is in these lists will dictate if
+   gas/ld will accept its presence in the architecture string.
+
+   Please add the extensions to the lists in lower case.  However, keep
+   these subsets in alphabetical order in these tables is recommended,
+   although there is no impact on the current implementation.  */
+
+static const char * const riscv_std_z_ext_strtab[] =
+{
+  "zba", "zbb", "zbc", "zicsr", "zifencei", "zihintpause", NULL
+};
+
+static const char * const riscv_std_s_ext_strtab[] =
+{
+  NULL
+};
+
+static const char * const riscv_std_h_ext_strtab[] =
+{
+  NULL
+};
+
+static const char * const riscv_std_zxm_ext_strtab[] =
+{
+  NULL
+};
+
+/* ISA extension prefixed name class.  Must define them in parsing order.  */
+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
+};
+
+/* Record the strings of the prefixed extensions, and their corresponding
+   classes.  The more letters of the prefix string, the more forward it must
+   be defined.  Otherwise, the riscv_get_prefix_class will map it to the
+   wrong classes.  */
+struct riscv_parse_prefix_config
+{
+  /* Class of the extension. */
+  enum riscv_prefix_ext_class class;
+
+  /* Prefix string for error printing and internal parser usage.  */
+  const char *prefix;
+};
+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}
+};
+
+/* Get the prefixed name class for the extensions, the class also
+   means the order of the prefixed extensions.  */
+
+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)
+    {
+      if (strncmp (arch, parse_config[i].prefix,
+		   strlen (parse_config[i].prefix)) == 0)
+	return parse_config[i].class;
+      i++;
+    }
+  return RV_ISA_CLASS_UNKNOWN;
+}
+
+/* Check KNOWN_EXTS to see if the EXT is supported.  */
+
+static bool
+riscv_known_prefixed_ext (const char *ext,
+			  const char *const *known_exts)
+{
+  size_t i;
+  for (i = 0; known_exts[i]; ++i)
+    if (strcmp (ext, known_exts[i]) == 0)
+      return true;
+  return false;
+}
 
-   Zero value: Preserved keyword.
-   Negative value: Prefixed keyword (s, h, x, z).
-   Positive value: Standard extension.  */
+/* Check whether the prefixed extension is valid or not.  Return
+   true if valid, otehrwise return false.  */
+
+static bool
+riscv_valid_prefixed_ext (const char *ext)
+{
+  enum riscv_prefix_ext_class class = riscv_get_prefix_class (ext);
+  switch (class)
+  {
+  case RV_ISA_CLASS_Z:
+    return riscv_known_prefixed_ext (ext, riscv_std_z_ext_strtab);
+  case RV_ISA_CLASS_ZXM:
+    return riscv_known_prefixed_ext (ext, riscv_std_zxm_ext_strtab);
+  case RV_ISA_CLASS_S:
+    return riscv_known_prefixed_ext (ext, riscv_std_s_ext_strtab);
+  case RV_ISA_CLASS_H:
+    return riscv_known_prefixed_ext (ext, riscv_std_h_ext_strtab);
+  case RV_ISA_CLASS_X:
+    /* Only the single x is invalid.  */
+    if (strcmp (ext, "x") != 0)
+      return true;
+  default:
+    break;
+  }
+  return false;
+}
+
+/* Array is used to compare the orders of standard extensions quickly.  */
 static int riscv_ext_order[26] = {0};
 
+/* Init the riscv_ext_order array.  */
+
+static void
+riscv_init_ext_order (void)
+{
+  static bool inited = false;
+  const char *std_base_exts = "eig";
+  const char *std_remain_exts = riscv_supported_std_ext ();
+  const char *ext;
+  int order;
+
+  if (inited)
+    return;
+
+  /* The orders of all standard extensions are positive.  */
+  order = 1;
+
+  /* Init the standard base extensions first.  */
+  for (ext = std_base_exts; *ext; ext++)
+    riscv_ext_order[(*ext - 'a')] = order++;
+
+  /* Init the standard remaining extensions.  */
+  for (ext = std_remain_exts; *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,
+     not through the riscv_ext_order.  */
+
+  inited = true;
+}
+
 /* Similar to the strcmp.  It returns an integer less than, equal to,
    or greater than zero if `subset2` is found, respectively, to be less
-   than, to match, or be greater than `subset1`.  */
+   than, to match, or be greater than `subset1`.
+
+   The order values,
+   Value 0: Preserved keywords.
+   Value 1 to 26: Standard extensions.
+   Value > 26: Prefixed keywords.  */
 
 int
 riscv_compare_subsets (const char *subset1, const char *subset2)
@@ -1049,10 +1200,19 @@  riscv_compare_subsets (const char *subset1, const char *subset2)
   if (order1 > 0 && order2 > 0)
     return order1 - order2;
 
-  if (order1 == order2 && order1 < 0)
+  /* Set the prefixed orders to negative values.  */
+  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)
+    order1 = - (int) class1;
+  if (class2 != RV_ISA_CLASS_UNKNOWN)
+    order2 = - (int) class2;
+
+  if (order1 == order2)
     {
       /* Compare the standard addition z extensions.  */
-      if (*subset1 == 'z')
+      if (class1 == RV_ISA_CLASS_Z)
 	{
 	  order1 = riscv_ext_order[(*++subset1 - 'a')];
 	  order2 = riscv_ext_order[(*++subset2 - 'a')];
@@ -1403,7 +1563,9 @@  riscv_parse_std_ext (riscv_parse_subset_t *rps,
 
   while (p != NULL && *p != '\0')
     {
-      if (*p == 'x' || *p == 's' || *p == 'h' || *p == 'z')
+      /* 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 == '_')
@@ -1419,10 +1581,10 @@  riscv_parse_std_ext (riscv_parse_subset_t *rps,
 
       if (std_ext != *std_exts)
 	{
-	  if (strchr (all_std_exts, std_ext) == NULL)
+	  if (riscv_ext_order[(std_ext - 'a')] == 0)
 	    rps->error_handler
-	      (_("-march=%s: unknown standard ISA extension `%c'"),
-	       march, std_ext);
+	      (_("-march=%s: unknown standard and prefixed ISA "
+		 "extension `%s'"), march, p);
 	  else
 	    rps->error_handler
 	      (_("-march=%s: standard ISA extension `%c' is not "
@@ -1443,38 +1605,6 @@  riscv_parse_std_ext (riscv_parse_subset_t *rps,
   return p;
 }
 
-/* Classify ARCH into one of riscv_isa_ext_class_t.  */
-
-riscv_isa_ext_class_t
-riscv_get_prefix_class (const char *arch)
-{
-  switch (*arch)
-    {
-    case 's': return RV_ISA_CLASS_S;
-    case 'h': return RV_ISA_CLASS_H;
-    case 'x': return RV_ISA_CLASS_X;
-    case 'z': return RV_ISA_CLASS_Z;
-    default: return RV_ISA_CLASS_UNKNOWN;
-    }
-}
-
-/* Structure describing parameters to use when parsing a particular
-   riscv_isa_ext_class_t.  One of these should be provided for each
-   possible class, except RV_ISA_CLASS_UNKNOWN.  */
-typedef struct riscv_parse_config
-{
-  /* Class of the extension. */
-  riscv_isa_ext_class_t class;
-
-  /* Prefix string for error printing and internal parser usage.  */
-  const char *prefix;
-
-  /* Predicate which is used for checking whether this is a "known"
-     extension. For 'x', it always returns true since they are by
-     definition non-standard and cannot be known.  */
-  bool (*ext_valid_p) (const char *);
-} riscv_parse_config_t;
-
 /* Parsing function for prefixed extensions.
 
    Return Value:
@@ -1490,13 +1620,12 @@  typedef struct riscv_parse_config
 static const char *
 riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
 			  const char *march,
-			  const char *p,
-			  const riscv_parse_config_t *config)
+			  const char *p)
 {
   int major_version;
   int minor_version;
   const char *last_name;
-  riscv_isa_ext_class_t class;
+  enum riscv_prefix_ext_class class;
 
   while (*p)
     {
@@ -1506,12 +1635,14 @@  riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
 	  continue;
 	}
 
-      /* Assert that the current extension specifier matches our parsing
-	 class.  */
       class = riscv_get_prefix_class (p);
-      if (class != config->class
-	  || class == RV_ISA_CLASS_UNKNOWN)
-	break;
+      if (class == RV_ISA_CLASS_UNKNOWN)
+	{
+	  rps->error_handler
+	    (_("-march=%s: unknown prefix class for the ISA extension `%s'"),
+	     march, p);
+	  return NULL;
+	}
 
       char *subset = xstrdup (p);
       char *q = subset;
@@ -1532,18 +1663,18 @@  riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
 	  return NULL;
 	}
 
-      /* Check that the prefix extension is known.
+      /* Check if the prefix extension is known.
 	 For 'x', anything goes but it cannot simply be 'x'.
 	 For 's', it must be known from a list and cannot simply be 's'.
 	 For 'h', it must be known from a list and cannot simply be 'h'.
 	 For 'z', it must be known from a list and cannot simply be 'z'.  */
 
       /* Check that the extension name is well-formed.  */
-      if (!config->ext_valid_p (subset))
+      if (!riscv_valid_prefixed_ext (subset))
 	{
 	  rps->error_handler
-	    (_("-march=%s: unknown %s ISA extension `%s'"),
-	     march, config->prefix, subset);
+	    (_("-march=%s: unknown prefixed ISA extension `%s'"),
+	     march, subset);
 	  free (subset);
 	  return NULL;
 	}
@@ -1553,19 +1684,19 @@  riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
       if (!strcasecmp (last_name, subset))
 	{
 	  rps->error_handler
-	    (_("-march=%s: duplicate %s ISA extension `%s'"),
-	     march, config->prefix, subset);
+	    (_("-march=%s: duplicate prefixed ISA extension `%s'"),
+	     march, subset);
 	  free (subset);
 	  return NULL;
 	}
 
-      /* Check that the extension is in alphabetical order.  */
+      /* Check that the extension is in expected order.  */
       if (riscv_compare_subsets (last_name, subset) > 0)
 	{
 	  rps->error_handler
-	    (_("-march=%s: %s ISA extension `%s' is not in alphabetical "
+	    (_("-march=%s: prefixed ISA extension `%s' is not in expected "
 	       "order.  It must come before `%s'"),
-	     march, config->prefix, subset, last_name);
+	     march, subset, last_name);
 	  free (subset);
 	  return NULL;
 	}
@@ -1579,8 +1710,8 @@  riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
       if (*p != '\0' && *p != '_')
 	{
 	  rps->error_handler
-	    (_("-march=%s: %s ISA extension must separate with _"),
-	     march, config->prefix);
+	    (_("-march=%s: prefixed ISA extension must separate with _"),
+	     march);
 	  return NULL;
 	}
     }
@@ -1588,132 +1719,6 @@  riscv_parse_prefixed_ext (riscv_parse_subset_t *rps,
   return p;
 }
 
-/* Lists of prefixed class extensions that binutils should know about.
-   Whether or not a particular entry is in these lists will dictate if
-   gas/ld will accept its presence in the architecture string.
-
-   Please add the extensions to the lists in lower case.  However, keep
-   these subsets in alphabetical order in these tables is recommended,
-   although there is no impact on the current implementation.  */
-
-static const char * const riscv_std_z_ext_strtab[] =
-{
-  "zba", "zbb", "zbc", "zicsr", "zifencei", "zihintpause", NULL
-};
-
-static const char * const riscv_std_s_ext_strtab[] =
-{
-  NULL
-};
-
-static const char * const riscv_std_h_ext_strtab[] =
-{
-  NULL
-};
-
-/* For the extension `ext`, search through the list of known extensions
-   `known_exts` for a match, and return TRUE if found.  */
-
-static bool
-riscv_multi_letter_ext_valid_p (const char *ext,
-				const char *const *known_exts)
-{
-  size_t i;
-
-  for (i = 0; known_exts[i]; ++i)
-    if (!strcmp (ext, known_exts[i]))
-      return true;
-
-  return false;
-}
-
-/* Predicator function for x-prefixed extensions.
-   Anything goes, except the literal 'x'.  */
-
-static bool
-riscv_ext_x_valid_p (const char *arg)
-{
-  if (!strcasecmp (arg, "x"))
-    return false;
-
-  return true;
-}
-
-/* Predicator functions for z-prefixed extensions.
-   Only known z-extensions are permitted.  */
-
-static bool
-riscv_ext_z_valid_p (const char *arg)
-{
-  return riscv_multi_letter_ext_valid_p (arg, riscv_std_z_ext_strtab);
-}
-
-/* Predicator function for 's' prefixed extensions.
-   Only known s-extensions are permitted.  */
-
-static bool
-riscv_ext_s_valid_p (const char *arg)
-{
-  return riscv_multi_letter_ext_valid_p (arg, riscv_std_s_ext_strtab);
-}
-
-/* Predicator function for 'h' prefixed extensions.
-   Only known h-extensions are permitted.  */
-
-static bool
-riscv_ext_h_valid_p (const char *arg)
-{
-  return riscv_multi_letter_ext_valid_p (arg, riscv_std_h_ext_strtab);
-}
-
-/* Parsing order of the prefixed extensions that is specified by
-   the ISA spec.  */
-static const riscv_parse_config_t parse_config[] =
-{
-  {RV_ISA_CLASS_S, "s", riscv_ext_s_valid_p},
-  {RV_ISA_CLASS_H, "h", riscv_ext_h_valid_p},
-  {RV_ISA_CLASS_Z, "z", riscv_ext_z_valid_p},
-  {RV_ISA_CLASS_X, "x", riscv_ext_x_valid_p},
-  {RV_ISA_CLASS_UNKNOWN, NULL, NULL}
-};
-
-/* Init the riscv_ext_order array.  */
-
-static void
-riscv_init_ext_order (void)
-{
-  static bool inited = false;
-  const char *std_base_exts = "eig";
-  const char *std_remain_exts = riscv_supported_std_ext ();
-  const char *ext;
-  unsigned int i;
-  int order;
-
-  if (inited)
-    return;
-
-  /* The orders of all standard extensions are positive.  */
-  order = 1;
-
-  /* Init the standard base extensions first.  */
-  for (ext = std_base_exts; *ext; ext++)
-    riscv_ext_order[(*ext - 'a')] = order++;
-
-  /* Init the standard remaining extensions.  */
-  for (ext = std_remain_exts; *ext; ext++)
-    riscv_ext_order[(*ext - 'a')] = order++;
-
-  /* Init the order for prefixed keywords.  The orders are negative.  */
-  order = -1;
-  for (i = 0; parse_config[i].class != RV_ISA_CLASS_UNKNOWN; i++)
-    {
-      ext = parse_config[i].prefix;
-      riscv_ext_order[(*ext - 'a')] = order--;
-    }
-
-  inited = true;
-}
-
 /* Add the implicit extensions.  */
 
 static void
@@ -1787,7 +1792,6 @@  riscv_parse_subset (riscv_parse_subset_t *rps,
 {
   riscv_subset_t *subset = NULL;
   const char *p;
-  size_t i;
   bool no_conflict = true;
 
   for (p = arch; *p != '\0'; p++)
@@ -1837,19 +1841,12 @@  riscv_parse_subset (riscv_parse_subset_t *rps,
     return false;
 
   /* Parse the different classes of extensions in the specified order.  */
-  for (i = 0; i < ARRAY_SIZE (parse_config); ++i)
+  while (*p != '\0')
     {
-      p = riscv_parse_prefixed_ext (rps, arch, p, &parse_config[i]);
+      p = riscv_parse_prefixed_ext (rps, arch, p);
 
       if (p == NULL)
-	return false;
-    }
-
-  if (*p != '\0')
-    {
-      rps->error_handler (_("-march=%s: unexpected ISA string at end: %s"),
-			  arch, p);
-      return false;
+        return false;
     }
 
   /* Finally add implicit extensions according to the current
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index 10b78ee..2955b75 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -90,19 +90,6 @@  riscv_arch_str (unsigned, const riscv_subset_list_t *);
 extern size_t
 riscv_estimate_digit (unsigned);
 
-/* ISA extension prefixed name class.  */
-typedef enum riscv_isa_ext_class
-{
-  RV_ISA_CLASS_S,
-  RV_ISA_CLASS_H,
-  RV_ISA_CLASS_Z,
-  RV_ISA_CLASS_X,
-  RV_ISA_CLASS_UNKNOWN
-} riscv_isa_ext_class_t;
-
-riscv_isa_ext_class_t
-riscv_get_prefix_class (const char *);
-
 extern int
 riscv_compare_subsets (const char *, const char *);
 
diff --git a/gas/testsuite/gas/riscv/march-fail-order-x-std.d b/gas/testsuite/gas/riscv/march-fail-order-x-std.d
new file mode 100644
index 0000000..4762f3d
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-order-x-std.d
@@ -0,0 +1,3 @@ 
+#as: -march=rv32i_xargle2p0_mafd
+#source: empty.s
+#error_output: march-fail-order-zx-std.l
diff --git a/gas/testsuite/gas/riscv/march-fail-order-x-z.d b/gas/testsuite/gas/riscv/march-fail-order-x-z.d
new file mode 100644
index 0000000..7245e68
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-order-x-z.d
@@ -0,0 +1,3 @@ 
+#as: -march=rv32i_xargle2p0_zicsr2p0
+#source: empty.s
+#error_output: march-fail-order-x-z.l
diff --git a/gas/testsuite/gas/riscv/march-fail-order-x-z.l b/gas/testsuite/gas/riscv/march-fail-order-x-z.l
new file mode 100644
index 0000000..53ea820
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-order-x-z.l
@@ -0,0 +1,2 @@ 
+.*Assembler messages:
+.*Error: .*prefixed ISA extension `zicsr' is not in expected order.  It must come before `xargle'
diff --git a/gas/testsuite/gas/riscv/march-fail-order-x.l b/gas/testsuite/gas/riscv/march-fail-order-x.l
index 025db14..cfb1185 100644
--- a/gas/testsuite/gas/riscv/march-fail-order-x.l
+++ b/gas/testsuite/gas/riscv/march-fail-order-x.l
@@ -1,2 +1,2 @@ 
 .*Assembler messages:
-.*Error: .*x ISA extension `xargle' is not in alphabetical order.  It must come before `xbargle'
+.*Error: .*prefixed ISA extension `xargle' is not in expected order.  It must come before `xbargle'
diff --git a/gas/testsuite/gas/riscv/march-fail-order-z-std.d b/gas/testsuite/gas/riscv/march-fail-order-z-std.d
new file mode 100644
index 0000000..42526de
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-order-z-std.d
@@ -0,0 +1,3 @@ 
+#as: -march=rv32i_zicsr2p0_mafd
+#source: empty.s
+#error_output: march-fail-order-zx-std.l
diff --git a/gas/testsuite/gas/riscv/march-fail-order-z.l b/gas/testsuite/gas/riscv/march-fail-order-z.l
index a98c53a..468c412 100644
--- a/gas/testsuite/gas/riscv/march-fail-order-z.l
+++ b/gas/testsuite/gas/riscv/march-fail-order-z.l
@@ -1,2 +1,2 @@ 
 .*Assembler messages:
-.*Error: .*z ISA extension `zicsr' is not in alphabetical order.  It must come before `zifencei'
+.*Error: .*prefixed ISA extension `zicsr' is not in expected order.  It must come before `zifencei'
diff --git a/gas/testsuite/gas/riscv/march-fail-order-zx-std.l b/gas/testsuite/gas/riscv/march-fail-order-zx-std.l
new file mode 100644
index 0000000..4f6b98c
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-order-zx-std.l
@@ -0,0 +1,2 @@ 
+.*Assembler messages:
+.*Error: .*unknown prefix class for the ISA extension `mafd'
diff --git a/gas/testsuite/gas/riscv/march-fail-porder-x-std.d b/gas/testsuite/gas/riscv/march-fail-porder-x-std.d
deleted file mode 100644
index 2bef073..0000000
--- a/gas/testsuite/gas/riscv/march-fail-porder-x-std.d
+++ /dev/null
@@ -1,3 +0,0 @@ 
-#as: -march=rv32i_xargle2p0_mafd
-#source: empty.s
-#error_output: march-fail-porder.l
diff --git a/gas/testsuite/gas/riscv/march-fail-porder-x-z.d b/gas/testsuite/gas/riscv/march-fail-porder-x-z.d
deleted file mode 100644
index 094180d..0000000
--- a/gas/testsuite/gas/riscv/march-fail-porder-x-z.d
+++ /dev/null
@@ -1,3 +0,0 @@ 
-#as: -march=rv32i_xargle2p0_zicsr2p0
-#source: empty.s
-#error_output: march-fail-porder.l
diff --git a/gas/testsuite/gas/riscv/march-fail-porder-z-std.d b/gas/testsuite/gas/riscv/march-fail-porder-z-std.d
deleted file mode 100644
index 1fa1a15..0000000
--- a/gas/testsuite/gas/riscv/march-fail-porder-z-std.d
+++ /dev/null
@@ -1,3 +0,0 @@ 
-#as: -march=rv32i_zicsr2p0_mafd
-#source: empty.s
-#error_output: march-fail-porder.l
diff --git a/gas/testsuite/gas/riscv/march-fail-porder.l b/gas/testsuite/gas/riscv/march-fail-porder.l
deleted file mode 100644
index c5496ea..0000000
--- a/gas/testsuite/gas/riscv/march-fail-porder.l
+++ /dev/null
@@ -1,2 +0,0 @@ 
-.*Assembler messages:
-.*Error: .*unexpected ISA string at end:.*
diff --git a/gas/testsuite/gas/riscv/march-fail-single-char-h.d b/gas/testsuite/gas/riscv/march-fail-single-char-h.d
deleted file mode 100644
index 7fca957..0000000
--- a/gas/testsuite/gas/riscv/march-fail-single-char-h.d
+++ /dev/null
@@ -1,3 +0,0 @@ 
-#as: -march=rv32ih
-#source: empty.s
-#error_output: march-fail-single-char.l
diff --git a/gas/testsuite/gas/riscv/march-fail-single-char-s.d b/gas/testsuite/gas/riscv/march-fail-single-char-s.d
deleted file mode 100644
index b3aace9..0000000
--- a/gas/testsuite/gas/riscv/march-fail-single-char-s.d
+++ /dev/null
@@ -1,3 +0,0 @@ 
-#as: -march=rv32is
-#source: empty.s
-#error_output: march-fail-single-char.l
diff --git a/gas/testsuite/gas/riscv/march-fail-single-char-x.d b/gas/testsuite/gas/riscv/march-fail-single-char-x.d
deleted file mode 100644
index 585608c..0000000
--- a/gas/testsuite/gas/riscv/march-fail-single-char-x.d
+++ /dev/null
@@ -1,3 +0,0 @@ 
-#as: -march=rv32ix
-#source: empty.s
-#error_output: march-fail-single-char.l
diff --git a/gas/testsuite/gas/riscv/march-fail-single-char-z.d b/gas/testsuite/gas/riscv/march-fail-single-char-z.d
deleted file mode 100644
index daf96d2..0000000
--- a/gas/testsuite/gas/riscv/march-fail-single-char-z.d
+++ /dev/null
@@ -1,3 +0,0 @@ 
-#as: -march=rv32iz
-#source: empty.s
-#error_output: march-fail-single-char.l
diff --git a/gas/testsuite/gas/riscv/march-fail-single-char.l b/gas/testsuite/gas/riscv/march-fail-single-char.l
deleted file mode 100644
index 435d0b2..0000000
--- a/gas/testsuite/gas/riscv/march-fail-single-char.l
+++ /dev/null
@@ -1,2 +0,0 @@ 
-.*Assembler messages:
-.*Error: .*unknown (s|h|z|x) ISA extension `(s|h|z|x)'
diff --git a/gas/testsuite/gas/riscv/march-fail-single-prefix-h.d b/gas/testsuite/gas/riscv/march-fail-single-prefix-h.d
new file mode 100644
index 0000000..eb101bd
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-single-prefix-h.d
@@ -0,0 +1,3 @@ 
+#as: -march=rv32ih
+#source: empty.s
+#error_output: march-fail-single-prefix.l
diff --git a/gas/testsuite/gas/riscv/march-fail-single-prefix-s.d b/gas/testsuite/gas/riscv/march-fail-single-prefix-s.d
new file mode 100644
index 0000000..8b6b0d0
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-single-prefix-s.d
@@ -0,0 +1,3 @@ 
+#as: -march=rv32is
+#source: empty.s
+#error_output: march-fail-single-prefix.l
diff --git a/gas/testsuite/gas/riscv/march-fail-single-prefix-x.d b/gas/testsuite/gas/riscv/march-fail-single-prefix-x.d
new file mode 100644
index 0000000..9e87801
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-single-prefix-x.d
@@ -0,0 +1,3 @@ 
+#as: -march=rv32ix
+#source: empty.s
+#error_output: march-fail-single-prefix.l
diff --git a/gas/testsuite/gas/riscv/march-fail-single-prefix-z.d b/gas/testsuite/gas/riscv/march-fail-single-prefix-z.d
new file mode 100644
index 0000000..4ae2692
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-single-prefix-z.d
@@ -0,0 +1,3 @@ 
+#as: -march=rv32iz
+#source: empty.s
+#error_output: march-fail-single-prefix.l
diff --git a/gas/testsuite/gas/riscv/march-fail-single-prefix-zxm.d b/gas/testsuite/gas/riscv/march-fail-single-prefix-zxm.d
new file mode 100644
index 0000000..bdf6f86
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-single-prefix-zxm.d
@@ -0,0 +1,3 @@ 
+#as: -march=rv32izxm
+#source: empty.s
+#error_output: march-fail-single-prefix.l
diff --git a/gas/testsuite/gas/riscv/march-fail-single-prefix.l b/gas/testsuite/gas/riscv/march-fail-single-prefix.l
new file mode 100644
index 0000000..13942ed
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-single-prefix.l
@@ -0,0 +1,2 @@ 
+.*Assembler messages:
+.*Error: .*unknown prefixed ISA extension `(s|h|z|x|zxm)'
diff --git a/gas/testsuite/gas/riscv/march-fail-unknown-std.l b/gas/testsuite/gas/riscv/march-fail-unknown-std.l
index 75cdda3..0e9add7 100644
--- a/gas/testsuite/gas/riscv/march-fail-unknown-std.l
+++ b/gas/testsuite/gas/riscv/march-fail-unknown-std.l
@@ -1,2 +1,2 @@ 
 .*Assembler messages:
-.*Error: .*unknown standard ISA extension `[^eimafdqiglcbjtpvn]'
+.*Error: .*unknown standard and prefixed ISA extension `y'
diff --git a/gas/testsuite/gas/riscv/march-fail-unknown-zxm.d b/gas/testsuite/gas/riscv/march-fail-unknown-zxm.d
new file mode 100644
index 0000000..b3d4603
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-unknown-zxm.d
@@ -0,0 +1,3 @@ 
+#as: -march=rv32izxmfoo2p0
+#source: empty.s
+#error_output: march-fail-unknown.l
diff --git a/gas/testsuite/gas/riscv/march-fail-unknown.l b/gas/testsuite/gas/riscv/march-fail-unknown.l
index 874b8d4..0cc8096 100644
--- a/gas/testsuite/gas/riscv/march-fail-unknown.l
+++ b/gas/testsuite/gas/riscv/march-fail-unknown.l
@@ -1,2 +1,2 @@ 
 .*Assembler messages:
-.*Error: .*unknown (s|h|z) ISA extension `(s|h|z)foo'
+.*Error: .*unknown prefixed ISA extension `(s|h|z|zxm)foo'