From 0d72ff8ee2c4b3212b5754e8196b7b5bcaae18f0 Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng@gmail.com>
Date: Mon, 19 Nov 2018 15:08:14 +0800
Subject: [PATCH] RISC-V: Accept version, supervisor extension and more than
 one NSE for -march.

This patch has move all -march parsing logic into bfd, because we
will use those code in ELF attributes.
---
 bfd/ChangeLog                                      |  22 +
 bfd/elfxx-riscv.c                                  | 475 +++++++++++++++++++++
 bfd/elfxx-riscv.h                                  |  52 +++
 gas/ChangeLog                                      |  44 ++
 gas/config/tc-riscv.c                              | 163 +------
 gas/testsuite/gas/riscv/empty.s                    |   1 +
 gas/testsuite/gas/riscv/march-fail-rv32ef.d        |   3 +
 gas/testsuite/gas/riscv/march-fail-rv32ef.l        |   2 +
 gas/testsuite/gas/riscv/march-fail-rv32i.d         |   3 +
 gas/testsuite/gas/riscv/march-fail-rv32i.l         |   2 +
 gas/testsuite/gas/riscv/march-fail-rv32iam.d       |   3 +
 gas/testsuite/gas/riscv/march-fail-rv32iam.l       |   2 +
 gas/testsuite/gas/riscv/march-fail-rv32ic.d        |   3 +
 gas/testsuite/gas/riscv/march-fail-rv32ic.l        |   2 +
 gas/testsuite/gas/riscv/march-fail-rv32icx2p.d     |   3 +
 gas/testsuite/gas/riscv/march-fail-rv32icx2p.l     |   2 +
 gas/testsuite/gas/riscv/march-fail-rv32imc.d       |   3 +
 gas/testsuite/gas/riscv/march-fail-rv32imc.l       |   2 +
 gas/testsuite/gas/riscv/march-fail-rv64I.d         |   3 +
 gas/testsuite/gas/riscv/march-fail-rv64I.l         |   2 +
 gas/testsuite/gas/riscv/march-fail-rv64e.d         |   3 +
 gas/testsuite/gas/riscv/march-fail-rv64e.l         |   2 +
 gas/testsuite/gas/riscv/march-ok-g2.d              |   5 +
 gas/testsuite/gas/riscv/march-ok-g2_p1.d           |   5 +
 gas/testsuite/gas/riscv/march-ok-g2p0.d            |   5 +
 gas/testsuite/gas/riscv/march-ok-i2p0.d            |   5 +
 gas/testsuite/gas/riscv/march-ok-i2p0m2_a2f2.d     |   5 +
 .../gas/riscv/march-ok-nse-with-version.d          |   5 +
 gas/testsuite/gas/riscv/march-ok-s-with-version.d  |   5 +
 gas/testsuite/gas/riscv/march-ok-s.d               |   5 +
 gas/testsuite/gas/riscv/march-ok-sx.d              |   5 +
 gas/testsuite/gas/riscv/march-ok-two-nse.d         |   5 +
 include/ChangeLog                                  |   5 +
 include/opcode/riscv.h                             |   2 +-
 opcodes/ChangeLog                                  |   5 +
 opcodes/riscv-dis.c                                |   2 +-
 36 files changed, 719 insertions(+), 142 deletions(-)
 create mode 100644 gas/testsuite/gas/riscv/empty.s
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv32ef.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv32ef.l
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv32i.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv32i.l
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv32iam.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv32iam.l
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv32ic.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv32ic.l
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv32icx2p.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv32icx2p.l
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv32imc.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv32imc.l
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv64I.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv64I.l
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv64e.d
 create mode 100644 gas/testsuite/gas/riscv/march-fail-rv64e.l
 create mode 100644 gas/testsuite/gas/riscv/march-ok-g2.d
 create mode 100644 gas/testsuite/gas/riscv/march-ok-g2_p1.d
 create mode 100644 gas/testsuite/gas/riscv/march-ok-g2p0.d
 create mode 100644 gas/testsuite/gas/riscv/march-ok-i2p0.d
 create mode 100644 gas/testsuite/gas/riscv/march-ok-i2p0m2_a2f2.d
 create mode 100644 gas/testsuite/gas/riscv/march-ok-nse-with-version.d
 create mode 100644 gas/testsuite/gas/riscv/march-ok-s-with-version.d
 create mode 100644 gas/testsuite/gas/riscv/march-ok-s.d
 create mode 100644 gas/testsuite/gas/riscv/march-ok-sx.d
 create mode 100644 gas/testsuite/gas/riscv/march-ok-two-nse.d

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 3210b0c..2320f75 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,25 @@
+2018-12-03  Kito Cheng  <kito@andestech.com>
+
+	* bfd/elfxx-riscv.h (RISCV_DONT_CARE_VERSION): New macro.
+	(struct riscv_subset_t): New structure.
+	(riscv_subset_t): New typedef.
+	(riscv_subset_list_t): New structure.
+	(riscv_release_subset_list): New prototype.
+	(riscv_add_subset): Likewise.
+	(riscv_lookup_subset): Likewise.
+	(riscv_lookup_subset_version): Likewise.
+	(riscv_release_subset_list): Likewise.
+
+	* bfd/elfxx-riscv.c (riscv_parsing_subset_version): New function.
+	(riscv_supported_std_ext): Likewise.
+	(riscv_parse_std_ext): Likewise.
+	(riscv_parse_sv_or_non_std_ext): Likewise.
+	(riscv_parse_subset): Likewise.
+	(riscv_add_subset): Likewise.
+	(riscv_lookup_subset): Likewise.
+	(riscv_lookup_subset_version): Likewise.
+	(riscv_release_subset_list): Likewise.
+
 2018-12-01  H.J. Lu  <hongjiu.lu@intel.com>
 
 	PR ld/23930
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index 7d4f59f..a88c7fa 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -28,6 +28,7 @@
 #include "opcode/riscv.h"
 #include "libiberty.h"
 #include "elfxx-riscv.h"
+#include "safe-ctype.h"
 #include <stdint.h>
 
 #define MINUS_ONE ((bfd_vma)0 - 1)
@@ -1010,3 +1011,477 @@ riscv_elf_add_sub_reloc (bfd *abfd,
 
   return bfd_reloc_ok;
 }
+
+/* Parsing subset version.
+
+   Return Value:
+     Points to the end of version
+
+   Arguments:
+     `rps`: Hooks and status for parsing subset.
+     `march`: Full arch string.
+     `p`: Curent parsing position.
+     `major_version`: Parsing result of major version, using
+      default_major_version if version is not present in arch string.
+     `minor_version`: Parsing result of minor version, set to 0 if version is
+     not present in arch string, but set to `default_minor_version` if
+     `major_version` using default_major_version.
+     `default_major_version`: Default major version.
+     `default_minor_version`: Default minor version.
+     `std_ext_p`: True if parsing std extension.  */
+
+static const char *
+riscv_parsing_subset_version (riscv_parse_subset_t *rps,
+			      const char *march,
+			      const char *p,
+			      unsigned *major_version,
+			      unsigned *minor_version,
+			      unsigned default_major_version,
+			      unsigned default_minor_version,
+			      bfd_boolean std_ext_p)
+{
+  bfd_boolean major_p = TRUE;
+  unsigned version = 0;
+  unsigned major = 0;
+  unsigned minor = 0;
+  char np;
+
+  for (;*p; ++p)
+    {
+      if (*p == 'p')
+	{
+	  np = *(p + 1);
+
+	  if (!ISDIGIT (np))
+	    {
+	      /* Might be beginning of `p` extension.  */
+	      if (std_ext_p)
+		{
+		  *major_version = version;
+		  *minor_version = 0;
+		  return p;
+		}
+	      else
+		{
+		  rps->error_handler ("-march=%s: Expect number after `%dp'.",
+				      march, version);
+		  return NULL;
+		}
+	    }
+
+	  major = version;
+	  major_p = FALSE;
+	  version = 0;
+	}
+      else if (ISDIGIT (*p))
+	version = (version * 10) + (*p - '0');
+      else
+	break;
+    }
+
+  if (major_p)
+    major = version;
+  else
+    minor = version;
+
+  if (major == 0 && minor == 0)
+    {
+      /* We don't found any version string, use default version.  */
+      *major_version = default_major_version;
+      *minor_version = default_minor_version;
+    }
+  else
+    {
+      *major_version = major;
+      *minor_version = minor;
+    }
+  return p;
+}
+
+/* Return string which contain all supported standard extensions in
+   canonical order.  */
+
+const char *
+riscv_supported_std_ext (void)
+{
+  return "mafdqlcbjtpvn";
+}
+
+/* Parsing function for standard extensions.
+
+   Return Value:
+     Points to the end of extensions.
+
+   Arguments:
+     `rps`: Hooks and status for parsing subset.
+     `march`: Full arch string.
+     `p`: Curent parsing position.  */
+
+static const char *
+riscv_parse_std_ext (riscv_parse_subset_t *rps,
+		     const char *march, const char *p)
+{
+  const char *all_std_exts = riscv_supported_std_ext ();
+  const char *std_exts = all_std_exts;
+
+  unsigned major_version = 0;
+  unsigned minor_version = 0;
+  char std_ext = '\0';
+
+  /* First letter must start with i, e or g.  */
+  switch (*p)
+    {
+      case 'i':
+	p++;
+	p = riscv_parsing_subset_version (
+	      rps,
+	      march,
+	      p, &major_version, &minor_version,
+	      /* default_major_version= */ 2,
+	      /* default_minor_version= */ 0,
+	      /* std_ext_p= */TRUE);
+	riscv_add_subset (rps->subset_list, "i", major_version, minor_version);
+	break;
+
+      case 'e':
+	p++;
+	p = riscv_parsing_subset_version (
+	      rps,
+	      march,
+	      p, &major_version, &minor_version,
+	      /* default_major_version= */ 1,
+	      /* default_minor_version= */ 9,
+	      /* std_ext_p= */TRUE);
+
+	riscv_add_subset (rps->subset_list, "e", major_version, minor_version);
+	riscv_add_subset (rps->subset_list, "i", 2, 0);
+
+	if (*rps->xlen > 32)
+	  {
+	    rps->error_handler ("-march=%s: rv%de is not a valid base ISA",
+				march, *rps->xlen);
+	    return NULL;
+	  }
+
+	break;
+
+      case 'g':
+	p++;
+	p = riscv_parsing_subset_version (
+	      rps,
+	      march,
+	      p, &major_version, &minor_version,
+	      /* default_major_version= */ 2,
+	      /* default_minor_version= */ 0,
+	      /* std_ext_p= */TRUE);
+	riscv_add_subset (rps->subset_list, "i", major_version, minor_version);
+
+	for ( ; *std_exts != 'q'; std_exts++)
+	  {
+	    const char subset[] = {*std_exts, '\0'};
+	    riscv_add_subset (
+	      rps->subset_list, subset, major_version, minor_version);
+	  }
+	break;
+
+      default:
+	rps->error_handler (
+	  "-march=%s: first ISA subset must be `e', `i' or `g'", march);
+	return NULL;
+    }
+
+  while (*p)
+    {
+      char subset[2] = {0, 0};
+
+      if (*p == 'x' || *p == 's')
+	break;
+
+      if (*p == '_')
+	{
+	  p++;
+	  continue;
+	}
+
+      std_ext = *p;
+
+      /* Checking canonical order.  */
+      while (*std_exts && std_ext != *std_exts) std_exts++;
+
+      if (std_ext != *std_exts)
+	{
+	  if (strchr (all_std_exts, std_ext) == NULL)
+	    rps->error_handler (
+	      "-march=%s: unsupported ISA subset `%c'", march, *p);
+	  else
+	    rps->error_handler (
+	      "-march=%s: ISA string is not in canonical order. `%c'",
+	      march, *p);
+	  return NULL;
+	}
+
+      std_exts++;
+
+      p++;
+      p = riscv_parsing_subset_version (
+	    rps,
+	    march,
+	    p, &major_version, &minor_version,
+	    /* default_major_version= */ 2,
+	    /* default_minor_version= */ 0,
+	    /* std_ext_p= */TRUE);
+
+      subset[0] = std_ext;
+
+      riscv_add_subset (rps->subset_list, subset, major_version, minor_version);
+    }
+  return p;
+}
+
+/* Parsing function for non-standard and supervisor extensions.
+
+   Return Value:
+     Points to the end of extensions.
+
+   Arguments:
+     `rps`: Hooks and status for parsing subset.
+     `march`: Full arch string.
+     `p`: Curent parsing position.
+     `ext_type`: What kind of extensions, 'x', 's' or 'sx'.
+     `ext_type_str`: Full name for kind of extension.  */
+
+static const char *
+riscv_parse_sv_or_non_std_ext (riscv_parse_subset_t *rps,
+			       const char *march,
+			       const char *p,
+			       const char *ext_type,
+			       const char *ext_type_str)
+{
+  unsigned major_version = 0;
+  unsigned minor_version = 0;
+  size_t ext_type_len = strlen (ext_type);
+
+  while (*p)
+    {
+      if (*p == '_')
+	{
+	  p++;
+	  continue;
+	}
+
+      if (strncmp (p, ext_type, ext_type_len) != 0)
+	break;
+
+      /* It's non-standard supervisor extension if it prefix with sx.  */
+      if ((ext_type[0] == 's') && (ext_type_len == 1)
+	  && (*(p + 1) == 'x'))
+	break;
+
+      char *subset = xstrdup (p);
+      char *q = subset;
+      const char *end_of_version;
+
+      while (*++q != '\0' && *q != '_' && !ISDIGIT (*q))
+	;
+
+      end_of_version =
+	riscv_parsing_subset_version (
+	  rps,
+	  march,
+	  q, &major_version, &minor_version,
+	  /* default_major_version= */ 2,
+	  /* default_minor_version= */ 0,
+	  /* std_ext_p= */FALSE);
+
+      *q = '\0';
+
+      riscv_add_subset (rps->subset_list, subset, major_version, minor_version);
+      free (subset);
+      p += end_of_version - subset;
+
+      if (*p != '\0' && *p != '_')
+	{
+	  rps->error_handler ("-march=%s: %s must seperate with _",
+			      march, ext_type_str);
+	  return NULL;
+	}
+    }
+
+  return p;
+}
+
+/* Function for parsing arch string.
+
+   Return Value:
+     Return TRUE on success.
+
+   Arguments:
+     `rps`: Hooks and status for parsing subset.
+     `arch`: Arch string.  */
+
+bfd_boolean
+riscv_parse_subset (riscv_parse_subset_t *rps,
+		    const char *arch)
+{
+  const char *p = arch;
+
+  if (strncmp (p, "rv32", 4) == 0)
+    {
+      *rps->xlen = 32;
+      p += 4;
+    }
+  else if (strncmp (p, "rv64", 4) == 0)
+    {
+      *rps->xlen = 64;
+      p += 4;
+    }
+  else
+    {
+      rps->error_handler ("-march=%s: ISA string must begin with rv32 or rv64",
+			  arch);
+      return FALSE;
+    }
+
+  /* Parsing standard extension.  */
+  p = riscv_parse_std_ext (rps, arch, p);
+
+  if (p == NULL)
+    return FALSE;
+
+  /* Parsing non-standard extension.  */
+  p = riscv_parse_sv_or_non_std_ext (
+	rps, arch, p, "x", "non-standard extension");
+
+  if (p == NULL)
+    return FALSE;
+
+  /* Parsing supervisor extension.  */
+  p = riscv_parse_sv_or_non_std_ext (
+	rps, arch, p, "s", "supervisor extension");
+
+  if (p == NULL)
+    return FALSE;
+
+  /* Parsing non-standard supervisor extension.  */
+  p = riscv_parse_sv_or_non_std_ext (
+	rps, arch, p, "sx", "non-standard supervisor extension");
+
+  if (p == NULL)
+    return FALSE;
+
+  if (*p != '\0')
+    {
+      rps->error_handler ("-march=%s: unexpected ISA string at end: %s",
+			  arch, p);
+      return FALSE;
+    }
+
+  if (riscv_lookup_subset (rps->subset_list, "e")
+      && riscv_lookup_subset (rps->subset_list, "f"))
+    {
+      rps->error_handler ("-march=%s: rv32e does not support the `f' extension",
+			  arch);
+      return FALSE;
+    }
+
+  if (riscv_lookup_subset (rps->subset_list, "d")
+      && !riscv_lookup_subset (rps->subset_list, "f"))
+    {
+      rps->error_handler ("-march=%s: `d' extension requires `f' extension",
+			  arch);
+      return FALSE;
+    }
+
+  if (riscv_lookup_subset (rps->subset_list, "q")
+      && !riscv_lookup_subset (rps->subset_list, "d"))
+    {
+      rps->error_handler ("-march=%s: `q' extension requires `d' extension",
+			  arch);
+      return FALSE;
+    }
+
+  if (riscv_lookup_subset (rps->subset_list, "q") && *rps->xlen < 64)
+    {
+      rps->error_handler ("-march=%s: rv32 does not support the `q' extension",
+			  arch);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+/* Add new subset to list.  */
+
+void
+riscv_add_subset (riscv_subset_list_t *subset_list,
+		  const char *subset,
+		  int major, int minor)
+{
+  riscv_subset_t *s = xmalloc (sizeof *s);
+
+  if (subset_list->head == NULL)
+    subset_list->head = s;
+
+  s->name = xstrdup (subset);
+  s->major_version = major;
+  s->minor_version = minor;
+  s->next = NULL;
+
+  if (subset_list->tail != NULL)
+    subset_list->tail->next = s;
+
+  subset_list->tail = s;
+}
+
+/* Find subset in list without version checking, return NULL if not found.  */
+
+riscv_subset_t *
+riscv_lookup_subset (const riscv_subset_list_t *subset_list,
+		     const char *subset)
+{
+  return riscv_lookup_subset_version (
+	   subset_list, subset,
+	   RISCV_DONT_CARE_VERSION,
+	   RISCV_DONT_CARE_VERSION);
+}
+
+/* Find subset in list with version checking, return NULL if not found.  */
+
+riscv_subset_t *
+riscv_lookup_subset_version (const riscv_subset_list_t *subset_list,
+			     const char *subset,
+			     int major, int minor)
+{
+  riscv_subset_t *s;
+
+  for (s = subset_list->head; s != NULL; s = s->next)
+    if (strcasecmp (s->name, subset) == 0)
+      {
+	if ((major != RISCV_DONT_CARE_VERSION)
+	    && (s->major_version != major))
+	  return NULL;
+
+	if ((minor != RISCV_DONT_CARE_VERSION)
+	    && (s->minor_version != minor))
+	  return NULL;
+
+	return s;
+      }
+
+  return NULL;
+}
+
+/* Release subset list.  */
+
+void
+riscv_release_subset_list (riscv_subset_list_t *subset_list)
+{
+   while (subset_list->head != NULL)
+    {
+      riscv_subset_t *next = subset_list->head->next;
+      free ((void *)subset_list->head->name);
+      free (subset_list->head);
+      subset_list->head = next;
+    }
+
+  subset_list->tail = NULL;
+}
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index 836f05d..64e41b8 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -31,3 +31,55 @@ riscv_reloc_type_lookup (bfd *, bfd_reloc_code_real_type);
 
 extern reloc_howto_type *
 riscv_elf_rtype_to_howto (bfd *, unsigned int r_type);
+
+#define RISCV_DONT_CARE_VERSION -1
+
+/* The information of architecture attribute.  */
+struct riscv_subset_t
+{
+  const char *name;
+  int major_version;
+  int minor_version;
+  struct riscv_subset_t *next;
+};
+
+typedef struct riscv_subset_t riscv_subset_t;
+
+typedef struct {
+  riscv_subset_t *head;
+  riscv_subset_t *tail;
+} riscv_subset_list_t;
+
+extern void
+riscv_release_subset_list (riscv_subset_list_t *);
+
+extern void
+riscv_add_subset (riscv_subset_list_t *,
+		  const char *,
+		  int, int);
+
+extern riscv_subset_t *
+riscv_lookup_subset (const riscv_subset_list_t *,
+		     const char *);
+
+extern riscv_subset_t *
+riscv_lookup_subset_version (const riscv_subset_list_t *,
+			     const char *,
+			     int, int);
+
+typedef struct {
+  riscv_subset_list_t *subset_list;
+  void (*error_handler) (const char *,
+			 ...) ATTRIBUTE_PRINTF_1;
+  unsigned *xlen;
+} riscv_parse_subset_t;
+
+extern bfd_boolean
+riscv_parse_subset (riscv_parse_subset_t *,
+		    const char *);
+
+extern const char *
+riscv_supported_std_ext (void);
+
+extern void
+riscv_release_subset_list (riscv_subset_list_t *);
diff --git a/gas/ChangeLog b/gas/ChangeLog
index f497b57..499f65f 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,47 @@
+2018-12-03  Kito Cheng  <kito@andestech.com>
+
+	* config/tc-riscv.c: Include elfxx-riscv.h.
+	(struct riscv_subset): Removed.
+	(riscv_subsets): Change type to riscv_subset_list_t.
+	(riscv_subset_supports): Removed argument: xlen_required and move
+	logic into libbfd.
+	(riscv_multi_subset_supports): Removed argument: xlen_required.
+	(riscv_clear_subsets): Removed.
+	(riscv_add_subset): Ditto.
+	(riscv_set_arch): Extract parsing logic into libbfd.
+	(riscv_ip): Update argument for riscv_multi_subset_supports and
+	riscv_subset_supports. Update riscv_subsets due to struct definition
+	changed.
+	(riscv_after_parse_args): Update riscv_subsets due to struct
+	definition changed, update and argument for riscv_subset_supports.
+	* testsuite/gas/riscv/empty.s: New.
+	* testsuite/gas/riscv/march-fail-rv32ef.d: Likewise.
+	* testsuite/gas/riscv/march-fail-rv32ef.l: Likewise.
+	* testsuite/gas/riscv/march-fail-rv32i.d: Likewise.
+	* testsuite/gas/riscv/march-fail-rv32i.l: Likewise.
+	* testsuite/gas/riscv/march-fail-rv32iam.d: Likewise.
+	* testsuite/gas/riscv/march-fail-rv32iam.l: Likewise.
+	* testsuite/gas/riscv/march-fail-rv32ic.d: Likewise.
+	* testsuite/gas/riscv/march-fail-rv32ic.l: Likewise.
+	* testsuite/gas/riscv/march-fail-rv32icx2p.d: Likewise.
+	* testsuite/gas/riscv/march-fail-rv32icx2p.l: Likewise.
+	* testsuite/gas/riscv/march-fail-rv32imc.d: Likewise.
+	* testsuite/gas/riscv/march-fail-rv32imc.l: Likewise.
+	* testsuite/gas/riscv/march-fail-rv64I.d: Likewise.
+	* testsuite/gas/riscv/march-fail-rv64I.l: Likewise.
+	* testsuite/gas/riscv/march-fail-rv64e.d: Likewise.
+	* testsuite/gas/riscv/march-fail-rv64e.l: Likewise.
+	* testsuite/gas/riscv/march-ok-g2.d: Likewise.
+	* testsuite/gas/riscv/march-ok-g2p0.d: Likewise.
+	* testsuite/gas/riscv/march-ok-i2p0.d: Likewise.
+	* testsuite/gas/riscv/march-ok-nse-with-version.: Likewise.d
+	* testsuite/gas/riscv/march-ok-s-with-version.d: Likewise.
+	* testsuite/gas/riscv/march-ok-s.d: Likewise.
+	* testsuite/gas/riscv/march-ok-sx.d: Likewise.
+	* testsuite/gas/riscv/march-ok-two-nse.d: Likewise.
+	* testsuite/gas/riscv/march-ok-g2_p1.d: Likewise.
+	* testsuite/gas/riscv/march-ok-i2p0m2_a2f2.d: Likewise.
+
 2018-12-01  Alan Modra  <amodra@gmail.com>
 
 	PR 23938
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index 426343c..92ddf80 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -29,6 +29,7 @@
 #include "dwarf2dbg.h"
 #include "dw2gencfi.h"
 
+#include "bfd/elfxx-riscv.h"
 #include "elf/riscv.h"
 #include "opcode/riscv.h"
 
@@ -102,160 +103,41 @@ riscv_set_rve (bfd_boolean rve_value)
   riscv_opts.rve = rve_value;
 }
 
-struct riscv_subset
-{
-  const char *name;
-
-  struct riscv_subset *next;
-};
-
-static struct riscv_subset *riscv_subsets;
+static riscv_subset_list_t riscv_subsets;
 
 static bfd_boolean
-riscv_subset_supports (unsigned xlen_required, const char *feature)
+riscv_subset_supports (const char *feature)
 {
-  struct riscv_subset *s;
-
-  if (xlen_required && xlen != xlen_required)
-    return FALSE;
-
-  for (s = riscv_subsets; s != NULL; s = s->next)
-    if (strcasecmp (s->name, feature) == 0)
-      return TRUE;
+  if (riscv_opts.rvc && (strcasecmp (feature, "c") == 0))
+    return TRUE;
 
-  return FALSE;
+  return riscv_lookup_subset (&riscv_subsets, feature) != NULL;
 }
 
 static bfd_boolean
-riscv_multi_subset_supports (unsigned xlen_required, const char *features[])
+riscv_multi_subset_supports (const char *features[])
 {
   unsigned i = 0;
   bfd_boolean supported = TRUE;
 
   for (;features[i]; ++i)
-    supported = supported && riscv_subset_supports (xlen_required, features[i]);
+    supported = supported && riscv_subset_supports (features[i]);
 
   return supported;
 }
 
-static void
-riscv_clear_subsets (void)
-{
-  while (riscv_subsets != NULL)
-    {
-      struct riscv_subset *next = riscv_subsets->next;
-      free ((void *) riscv_subsets->name);
-      free (riscv_subsets);
-      riscv_subsets = next;
-    }
-}
-
-static void
-riscv_add_subset (const char *subset)
-{
-  struct riscv_subset *s = xmalloc (sizeof *s);
-
-  s->name = xstrdup (subset);
-  s->next = riscv_subsets;
-  riscv_subsets = s;
-}
-
 /* Set which ISA and extensions are available.  */
 
 static void
 riscv_set_arch (const char *s)
 {
-  const char *all_subsets = "imafdqc";
-  char *extension = NULL;
-  const char *p = s;
-
-  riscv_clear_subsets();
+  riscv_parse_subset_t rps;
+  rps.subset_list = &riscv_subsets;
+  rps.error_handler = as_fatal;
+  rps.xlen = &xlen;
 
-  if (strncmp (p, "rv32", 4) == 0)
-    {
-      xlen = 32;
-      p += 4;
-    }
-  else if (strncmp (p, "rv64", 4) == 0)
-    {
-      xlen = 64;
-      p += 4;
-    }
-  else
-    as_fatal ("-march=%s: ISA string must begin with rv32 or rv64", s);
-
-  switch (*p)
-    {
-      case 'i':
-	break;
-
-      case 'e':
-	p++;
-	riscv_add_subset ("e");
-	riscv_add_subset ("i");
-
-	if (xlen > 32)
-	  as_fatal ("-march=%s: rv%de is not a valid base ISA", s, xlen);
-
-	break;
-
-      case 'g':
-	p++;
-	for ( ; *all_subsets != 'q'; all_subsets++)
-	  {
-	    const char subset[] = {*all_subsets, '\0'};
-	    riscv_add_subset (subset);
-	  }
-	break;
-
-      default:
-	as_fatal ("-march=%s: first ISA subset must be `e', `i' or `g'", s);
-    }
-
-  while (*p)
-    {
-      if (*p == 'x')
-	{
-	  char *subset = xstrdup (p);
-	  char *q = subset;
-
-	  while (*++q != '\0' && *q != '_')
-	    ;
-	  *q = '\0';
-
-	  if (extension)
-	    as_fatal ("-march=%s: only one non-standard extension is supported"
-		      " (found `%s' and `%s')", s, extension, subset);
-	  extension = subset;
-	  riscv_add_subset (subset);
-	  p += strlen (subset);
-	}
-      else if (*p == '_')
-	p++;
-      else if ((all_subsets = strchr (all_subsets, *p)) != NULL)
-	{
-	  const char subset[] = {*p, 0};
-	  riscv_add_subset (subset);
-	  all_subsets++;
-	  p++;
-	}
-      else
-	as_fatal ("-march=%s: unsupported ISA subset `%c'", s, *p);
-    }
-
-  if (riscv_subset_supports (0, "e") && riscv_subset_supports (0, "f"))
-    as_fatal ("-march=%s: rv32e does not support the `f' extension", s);
-
-  if (riscv_subset_supports (0, "d") && !riscv_subset_supports (0, "f"))
-    as_fatal ("-march=%s: `d' extension requires `f' extension", s);
-
-  if (riscv_subset_supports (0, "q") && !riscv_subset_supports (0, "d"))
-    as_fatal ("-march=%s: `q' extension requires `d' extension", s);
-
-  if (riscv_subset_supports (0, "q") && xlen < 64)
-    as_fatal ("-march=%s: rv32 does not support the `q' extension", s);
-
-  free (extension);
+  riscv_release_subset_list (&riscv_subsets);
+  riscv_parse_subset (&rps, s);
 }
 
 /* Handle of the OPCODE hash table.  */
@@ -1491,7 +1373,10 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
   argsStart = s;
   for ( ; insn && insn->name && strcmp (insn->name, str) == 0; insn++)
     {
-      if (!riscv_multi_subset_supports (insn->xlen_requirement, insn->subset))
+      if ((insn->xlen_requirement != 0) && (xlen != insn->xlen_requirement))
+	continue;
+
+      if (!riscv_multi_subset_supports (insn->subset))
 	continue;
 
       create_insn (ip, insn);
@@ -2332,19 +2217,17 @@ riscv_after_parse_args (void)
 	as_bad ("unknown default architecture `%s'", default_arch);
     }
 
-  if (riscv_subsets == NULL)
+  if (riscv_subsets.head == NULL)
     riscv_set_arch (xlen == 64 ? "rv64g" : "rv32g");
 
   /* Add the RVC extension, regardless of -march, to support .option rvc.  */
   riscv_set_rvc (FALSE);
-  if (riscv_subset_supports (0, "c"))
+  if (riscv_subset_supports ("c"))
     riscv_set_rvc (TRUE);
-  else
-    riscv_add_subset ("c");
 
   /* Enable RVE if specified by the -march option.  */
   riscv_set_rve (FALSE);
-  if (riscv_subset_supports (0, "e"))
+  if (riscv_subset_supports ("e"))
     riscv_set_rve (TRUE);
 
   /* Infer ABI from ISA if not specified on command line.  */
@@ -2357,12 +2240,12 @@ riscv_after_parse_args (void)
 
   if (float_abi == FLOAT_ABI_DEFAULT)
     {
-      struct riscv_subset *subset;
+      riscv_subset_t *subset;
 
       /* Assume soft-float unless D extension is present.  */
       float_abi = FLOAT_ABI_SOFT;
 
-      for (subset = riscv_subsets; subset != NULL; subset = subset->next)
+      for (subset = riscv_subsets.head; subset != NULL; subset = subset->next)
 	{
 	  if (strcasecmp (subset->name, "D") == 0)
 	    float_abi = FLOAT_ABI_DOUBLE;
diff --git a/gas/testsuite/gas/riscv/empty.s b/gas/testsuite/gas/riscv/empty.s
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/gas/testsuite/gas/riscv/empty.s
@@ -0,0 +1 @@
+
diff --git a/gas/testsuite/gas/riscv/march-fail-rv32ef.d b/gas/testsuite/gas/riscv/march-fail-rv32ef.d
new file mode 100644
index 0000000..d7b51c3
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv32ef.d
@@ -0,0 +1,3 @@
+#as: -march=rv32ef
+#source: empty.s
+#error_output: march-fail-rv32ef.l
diff --git a/gas/testsuite/gas/riscv/march-fail-rv32ef.l b/gas/testsuite/gas/riscv/march-fail-rv32ef.l
new file mode 100644
index 0000000..15e56c8
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv32ef.l
@@ -0,0 +1,2 @@
+Assembler messages:
+Fatal error: -march=rv32ef: rv32e does not support the `f' extension
diff --git a/gas/testsuite/gas/riscv/march-fail-rv32i.d b/gas/testsuite/gas/riscv/march-fail-rv32i.d
new file mode 100644
index 0000000..1e6e9e0
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv32i.d
@@ -0,0 +1,3 @@
+#as: -march=rv32I
+#source: empty.s
+#error_output: march-fail-rv32i.l
diff --git a/gas/testsuite/gas/riscv/march-fail-rv32i.l b/gas/testsuite/gas/riscv/march-fail-rv32i.l
new file mode 100644
index 0000000..1977aed
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv32i.l
@@ -0,0 +1,2 @@
+Assembler messages:
+Fatal error: -march=rv32I: first ISA subset must be `e', `i' or `g'
diff --git a/gas/testsuite/gas/riscv/march-fail-rv32iam.d b/gas/testsuite/gas/riscv/march-fail-rv32iam.d
new file mode 100644
index 0000000..054cf4d
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv32iam.d
@@ -0,0 +1,3 @@
+#as: -march=rv32iam
+#source: empty.s
+#error_output: march-fail-rv32iam.l
diff --git a/gas/testsuite/gas/riscv/march-fail-rv32iam.l b/gas/testsuite/gas/riscv/march-fail-rv32iam.l
new file mode 100644
index 0000000..c7786f8
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv32iam.l
@@ -0,0 +1,2 @@
+Assembler messages:
+Fatal error: -march=rv32iam: ISA string is not in canonical order. `m'
diff --git a/gas/testsuite/gas/riscv/march-fail-rv32ic.d b/gas/testsuite/gas/riscv/march-fail-rv32ic.d
new file mode 100644
index 0000000..b419a65
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv32ic.d
@@ -0,0 +1,3 @@
+#as: -march=rv32iC
+#source: empty.s
+#error_output: march-fail-rv32ic.l
diff --git a/gas/testsuite/gas/riscv/march-fail-rv32ic.l b/gas/testsuite/gas/riscv/march-fail-rv32ic.l
new file mode 100644
index 0000000..d24ea2f
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv32ic.l
@@ -0,0 +1,2 @@
+Assembler messages:
+Fatal error: -march=rv32iC: unsupported ISA subset `C'
diff --git a/gas/testsuite/gas/riscv/march-fail-rv32icx2p.d b/gas/testsuite/gas/riscv/march-fail-rv32icx2p.d
new file mode 100644
index 0000000..5198090
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv32icx2p.d
@@ -0,0 +1,3 @@
+#as: -march=rv32icx2p
+#source: empty.s
+#error_output: march-fail-rv32icx2p.l
diff --git a/gas/testsuite/gas/riscv/march-fail-rv32icx2p.l b/gas/testsuite/gas/riscv/march-fail-rv32icx2p.l
new file mode 100644
index 0000000..25627d4
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv32icx2p.l
@@ -0,0 +1,2 @@
+Assembler messages:
+Fatal error: -march=rv32icx2p: Expect number after `2p'.
diff --git a/gas/testsuite/gas/riscv/march-fail-rv32imc.d b/gas/testsuite/gas/riscv/march-fail-rv32imc.d
new file mode 100644
index 0000000..1cb4d9e
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv32imc.d
@@ -0,0 +1,3 @@
+#as: -march=rv32iamfd
+#source: empty.s
+#error_output: march-fail-rv32imc.l
diff --git a/gas/testsuite/gas/riscv/march-fail-rv32imc.l b/gas/testsuite/gas/riscv/march-fail-rv32imc.l
new file mode 100644
index 0000000..d922e9d
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv32imc.l
@@ -0,0 +1,2 @@
+Assembler messages:
+Fatal error: -march=rv32iamfd: ISA string is not in canonical order. `m'
diff --git a/gas/testsuite/gas/riscv/march-fail-rv64I.d b/gas/testsuite/gas/riscv/march-fail-rv64I.d
new file mode 100644
index 0000000..e00a6f8
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv64I.d
@@ -0,0 +1,3 @@
+#as: -march=rv64I
+#source: empty.s
+#error_output: march-fail-rv64I.l
diff --git a/gas/testsuite/gas/riscv/march-fail-rv64I.l b/gas/testsuite/gas/riscv/march-fail-rv64I.l
new file mode 100644
index 0000000..5b46e77
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv64I.l
@@ -0,0 +1,2 @@
+Assembler messages:
+Fatal error: -march=rv64I: first ISA subset must be `e', `i' or `g'
diff --git a/gas/testsuite/gas/riscv/march-fail-rv64e.d b/gas/testsuite/gas/riscv/march-fail-rv64e.d
new file mode 100644
index 0000000..38d73db
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv64e.d
@@ -0,0 +1,3 @@
+#as: -march=rv64e
+#source: empty.s
+#error_output: march-fail-rv64e.l
diff --git a/gas/testsuite/gas/riscv/march-fail-rv64e.l b/gas/testsuite/gas/riscv/march-fail-rv64e.l
new file mode 100644
index 0000000..85f7554
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-fail-rv64e.l
@@ -0,0 +1,2 @@
+Assembler messages:
+Fatal error: -march=rv64e: rv64e is not a valid base ISA
diff --git a/gas/testsuite/gas/riscv/march-ok-g2.d b/gas/testsuite/gas/riscv/march-ok-g2.d
new file mode 100644
index 0000000..38541ad
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-ok-g2.d
@@ -0,0 +1,5 @@
+#as: -march=rv32g2
+#objdump: -dr
+#source: empty.s
+
+.*:     file format elf32-littleriscv
diff --git a/gas/testsuite/gas/riscv/march-ok-g2_p1.d b/gas/testsuite/gas/riscv/march-ok-g2_p1.d
new file mode 100644
index 0000000..cd9e127
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-ok-g2_p1.d
@@ -0,0 +1,5 @@
+#as: -march=rv32g2_p1
+#objdump: -dr
+#source: empty.s
+
+.*:     file format elf32-littleriscv
diff --git a/gas/testsuite/gas/riscv/march-ok-g2p0.d b/gas/testsuite/gas/riscv/march-ok-g2p0.d
new file mode 100644
index 0000000..b439314
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-ok-g2p0.d
@@ -0,0 +1,5 @@
+#as: -march=rv32g2p0
+#objdump: -dr
+#source: empty.s
+
+.*:     file format elf32-littleriscv
diff --git a/gas/testsuite/gas/riscv/march-ok-i2p0.d b/gas/testsuite/gas/riscv/march-ok-i2p0.d
new file mode 100644
index 0000000..eb8309c
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-ok-i2p0.d
@@ -0,0 +1,5 @@
+#as: -march=rv32i2p0
+#objdump: -dr
+#source: empty.s
+
+.*:     file format elf32-littleriscv
diff --git a/gas/testsuite/gas/riscv/march-ok-i2p0m2_a2f2.d b/gas/testsuite/gas/riscv/march-ok-i2p0m2_a2f2.d
new file mode 100644
index 0000000..6658417
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-ok-i2p0m2_a2f2.d
@@ -0,0 +1,5 @@
+#as: -march=rv32i2p0m2_a2f2
+#objdump: -dr
+#source: empty.s
+
+.*:     file format elf32-littleriscv
diff --git a/gas/testsuite/gas/riscv/march-ok-nse-with-version.d b/gas/testsuite/gas/riscv/march-ok-nse-with-version.d
new file mode 100644
index 0000000..bdca7fb
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-ok-nse-with-version.d
@@ -0,0 +1,5 @@
+#as: -march=rv32imafd_xargle2p0
+#objdump: -dr
+#source: empty.s
+
+.*:     file format elf32-littleriscv
diff --git a/gas/testsuite/gas/riscv/march-ok-s-with-version.d b/gas/testsuite/gas/riscv/march-ok-s-with-version.d
new file mode 100644
index 0000000..6296a15
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-ok-s-with-version.d
@@ -0,0 +1,5 @@
+#as: -march=rv32isfoo3p4
+#objdump: -dr
+#source: empty.s
+
+.*:     file format elf32-littleriscv
diff --git a/gas/testsuite/gas/riscv/march-ok-s.d b/gas/testsuite/gas/riscv/march-ok-s.d
new file mode 100644
index 0000000..7daa0a1
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-ok-s.d
@@ -0,0 +1,5 @@
+#as: -march=rv32isfoo
+#objdump: -dr
+#source: empty.s
+
+.*:     file format elf32-littleriscv
diff --git a/gas/testsuite/gas/riscv/march-ok-sx.d b/gas/testsuite/gas/riscv/march-ok-sx.d
new file mode 100644
index 0000000..e2172f2
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-ok-sx.d
@@ -0,0 +1,5 @@
+#as: -march=rv32isfoo_sxbar
+#objdump: -dr
+#source: empty.s
+
+.*:     file format elf32-littleriscv
diff --git a/gas/testsuite/gas/riscv/march-ok-two-nse.d b/gas/testsuite/gas/riscv/march-ok-two-nse.d
new file mode 100644
index 0000000..0fe5037
--- /dev/null
+++ b/gas/testsuite/gas/riscv/march-ok-two-nse.d
@@ -0,0 +1,5 @@
+#as: -march=rv32imafd_xargle_xbargle
+#objdump: -dr
+#source: empty.s
+
+.*:     file format elf32-littleriscv
diff --git a/include/ChangeLog b/include/ChangeLog
index 6424596..7375bac 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,3 +1,8 @@
+2018-12-03  Kito Cheng  <kito@andestech.com>
+
+	* opcode/riscv.h (riscv_opcode): Change type of xlen_requirement to
+	unsigned.
+
 2018-11-27  Jim Wilson  <jimw@sifive.com>
 
 	* opcode/riscv.h (OP_MASK_CFUNCT6, OP_SH_CFUNCT6): New.
diff --git a/include/opcode/riscv.h b/include/opcode/riscv.h
index 10c5f3d..12cb4ca 100644
--- a/include/opcode/riscv.h
+++ b/include/opcode/riscv.h
@@ -295,7 +295,7 @@ struct riscv_opcode
   /* The name of the instruction.  */
   const char *name;
   /* The requirement of xlen for the instruction, 0 if no requirement.  */
-  int xlen_requirement;
+  unsigned xlen_requirement;
   /* An array of ISA subset name (I, M, A, F, D, Xextension), must ended
      with a NULL pointer sential.  */
   const char *subset[MAX_SUBSET_NUM];
diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog
index 63560f1..34237bb 100644
--- a/opcodes/ChangeLog
+++ b/opcodes/ChangeLog
@@ -1,3 +1,8 @@
+2018-12-03  Kito Cheng <kito@andestech.com>
+
+	* riscv-opc.c: Change the type of xlen, because type of xlen_requirement
+	changed.
+
 2018-11-29  Jim Wilson  <jimw@sifive.com>
 
 	* riscv-opc.c (unimp): Mark compressed unimp as INSN_ALIAS.
diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c
index 890f1f8..9790820 100644
--- a/opcodes/riscv-dis.c
+++ b/opcodes/riscv-dis.c
@@ -408,7 +408,7 @@ riscv_disassemble_insn (bfd_vma memaddr, insn_t word, disassemble_info *info)
   op = riscv_hash[OP_HASH_IDX (word)];
   if (op != NULL)
     {
-      int xlen = 0;
+      unsigned xlen = 0;
 
       /* If XLEN is not known, get its value from the ELF class.  */
       if (info->mach == bfd_mach_riscv64)
-- 
1.8.3.1

