[1/2] RISC-V: Accept version, supervisor ext and more than one NSE for -march.

Message ID 1550042128-18022-2-git-send-email-kito@andestech.com
State New
Headers show
Series
  • RISC-V: Support ELF attribute for GCC.
Related show

Commit Message

Kito Cheng Feb. 13, 2019, 7:15 a.m.
From: Kito Cheng <kito.cheng@gmail.com>


Kito Cheng <kito.cheng@gmail.com>
Monk Chiang  <sh.chiang04@gmail.com>

ChangeLog:
gcc:
	* common/config/riscv/riscv-common.c:
	Include config/riscv/riscv-protos.h.
	(INCLUDE_STRING): Defined.
	(RISCV_DONT_CARE_VERSION): Defined.
	(riscv_subset_t): Declare.
	(riscv_subset_list): Declare.
	(riscv_subset_list::riscv_subset_list): New.
	(riscv_subset_list::~riscv_subset_list): Likewise.
	(riscv_subset_list::parsing_subset_version): Likewise.
	(riscv_subset_list::parse_std_ext): Likewise.
	(riscv_subset_list::parse_sv_or_non_std_ext): Likewise.
	(riscv_subset_list::add): Likewise.
	(riscv_subset_list::lookup): Likewise.
	(riscv_subset_list::xlen): Likewise.
	(riscv_subset_list::parse): Likewise.
	(riscv_supported_std_ext): Likewise.
	(current_subset_list): Likewise.
	(riscv_parse_arch_string): Using riscv_subset_list::parse to
	parse.

gcc/testsuite:
	* gcc.target/riscv/arch-1.c: New.
	* gcc.target/riscv/arch-2.c: Likewise.
	* gcc.target/riscv/arch-3.c: Likewise.
	* gcc.target/riscv/arch-4.c: Likewise.
---
 gcc/common/config/riscv/riscv-common.c  | 530 ++++++++++++++++++++++++++++----
 gcc/testsuite/gcc.target/riscv/arch-1.c |   7 +
 gcc/testsuite/gcc.target/riscv/arch-2.c |   6 +
 gcc/testsuite/gcc.target/riscv/arch-3.c |   6 +
 gcc/testsuite/gcc.target/riscv/arch-4.c |   6 +
 5 files changed, 496 insertions(+), 59 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/arch-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/arch-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/arch-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/arch-4.c

-- 
1.8.3.1

Comments

Kito Cheng Feb. 13, 2019, 7:19 a.m. | #1
Sorry for annoying, this patch should appear in gcc-patches@gcc.gnu.org. :(

On Wed, Feb 13, 2019 at 3:15 PM Kito Cheng <kito@andestech.com> wrote:
>

> From: Kito Cheng <kito.cheng@gmail.com>

>

> Kito Cheng <kito.cheng@gmail.com>

> Monk Chiang  <sh.chiang04@gmail.com>

>

> ChangeLog:

> gcc:

>         * common/config/riscv/riscv-common.c:

>         Include config/riscv/riscv-protos.h.

>         (INCLUDE_STRING): Defined.

>         (RISCV_DONT_CARE_VERSION): Defined.

>         (riscv_subset_t): Declare.

>         (riscv_subset_list): Declare.

>         (riscv_subset_list::riscv_subset_list): New.

>         (riscv_subset_list::~riscv_subset_list): Likewise.

>         (riscv_subset_list::parsing_subset_version): Likewise.

>         (riscv_subset_list::parse_std_ext): Likewise.

>         (riscv_subset_list::parse_sv_or_non_std_ext): Likewise.

>         (riscv_subset_list::add): Likewise.

>         (riscv_subset_list::lookup): Likewise.

>         (riscv_subset_list::xlen): Likewise.

>         (riscv_subset_list::parse): Likewise.

>         (riscv_supported_std_ext): Likewise.

>         (current_subset_list): Likewise.

>         (riscv_parse_arch_string): Using riscv_subset_list::parse to

>         parse.

>

> gcc/testsuite:

>         * gcc.target/riscv/arch-1.c: New.

>         * gcc.target/riscv/arch-2.c: Likewise.

>         * gcc.target/riscv/arch-3.c: Likewise.

>         * gcc.target/riscv/arch-4.c: Likewise.

> ---

>  gcc/common/config/riscv/riscv-common.c  | 530 ++++++++++++++++++++++++++++----

>  gcc/testsuite/gcc.target/riscv/arch-1.c |   7 +

>  gcc/testsuite/gcc.target/riscv/arch-2.c |   6 +

>  gcc/testsuite/gcc.target/riscv/arch-3.c |   6 +

>  gcc/testsuite/gcc.target/riscv/arch-4.c |   6 +

>  5 files changed, 496 insertions(+), 59 deletions(-)

>  create mode 100644 gcc/testsuite/gcc.target/riscv/arch-1.c

>  create mode 100644 gcc/testsuite/gcc.target/riscv/arch-2.c

>  create mode 100644 gcc/testsuite/gcc.target/riscv/arch-3.c

>  create mode 100644 gcc/testsuite/gcc.target/riscv/arch-4.c

>

> diff --git a/gcc/common/config/riscv/riscv-common.c b/gcc/common/config/riscv/riscv-common.c

> index cb5bb7f..e412acb 100644

> --- a/gcc/common/config/riscv/riscv-common.c

> +++ b/gcc/common/config/riscv/riscv-common.c

> @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License

>  along with GCC; see the file COPYING3.  If not see

>  <http://www.gnu.org/licenses/>.  */

>

> +#define INCLUDE_STRING

>  #include "config.h"

>  #include "system.h"

>  #include "coretypes.h"

> @@ -26,99 +27,510 @@ along with GCC; see the file COPYING3.  If not see

>  #include "opts.h"

>  #include "flags.h"

>  #include "diagnostic-core.h"

> +#include "config/riscv/riscv-protos.h"

>

> -/* Parse a RISC-V ISA string into an option mask.  Must clear or set all arch

> -   dependent mask bits, in case more than one -march string is passed.  */

> +#define RISCV_DONT_CARE_VERSION -1

>

> -static void

> -riscv_parse_arch_string (const char *isa, int *flags, location_t loc)

> +/* Subset info.  */

> +struct riscv_subset_t {

> +  riscv_subset_t ();

> +

> +  std::string name;

> +  int major_version;

> +  int minor_version;

> +  struct riscv_subset_t *next;

> +};

> +

> +/* Subset list.  */

> +class riscv_subset_list {

> +private:

> +  /* Original arch string.  */

> +  const char *m_arch;

> +

> +  /* Localtion of arch string, used for report error.  */

> +  location_t m_loc;

> +

> +  /* Head of subset info list.  */

> +  riscv_subset_t *m_head;

> +

> +  /* Tail of subset info list.  */

> +  riscv_subset_t *m_tail;

> +

> +  /* X-len of m_arch. */

> +  unsigned m_xlen;

> +

> +  riscv_subset_list (const char *, location_t);

> +

> +  const char *parsing_subset_version (const char *, unsigned *, unsigned *,

> +                                     unsigned, unsigned, bool);

> +

> +  const char *parse_std_ext (const char *);

> +

> +  const char *parse_sv_or_non_std_ext (const char *, const char *,

> +                                      const char *);

> +

> +public:

> +  ~riscv_subset_list ();

> +

> +  void add (const char *, int, int);

> +

> +  riscv_subset_t *lookup (const char *,

> +                         int major_version = RISCV_DONT_CARE_VERSION,

> +                         int minor_version = RISCV_DONT_CARE_VERSION) const;

> +

> +  unsigned xlen() const {return m_xlen;};

> +

> +  static riscv_subset_list *parse (const char *, location_t);

> +

> +};

> +

> +static const char *riscv_supported_std_ext (void);

> +

> +static riscv_subset_list *current_subset_list = NULL;

> +

> +riscv_subset_t::riscv_subset_t()

> +: name (), major_version (0), minor_version (0), next (NULL)

>  {

> -  const char *p = isa;

> +}

>

> -  if (strncmp (p, "rv32", 4) == 0)

> -    *flags &= ~MASK_64BIT, p += 4;

> -  else if (strncmp (p, "rv64", 4) == 0)

> -    *flags |= MASK_64BIT, p += 4;

> -  else

> +riscv_subset_list::riscv_subset_list (const char *arch, location_t loc)

> +: m_arch (arch), m_loc(loc), m_head (NULL), m_tail (NULL), m_xlen(0)

> +{

> +}

> +

> +riscv_subset_list::~riscv_subset_list()

> +{

> +  if (!m_head)

> +    return;

> +

> +  riscv_subset_t *item = this->m_head->next;

> +  while (item != NULL)

>      {

> -      error_at (loc, "-march=%s: ISA string must begin with rv32 or rv64", isa);

> -      return;

> +      riscv_subset_t *next = item->next;

> +      delete item;

> +      item = next;

>      }

> +}

>

> -  if (*p == 'g')

> -    {

> -      p++;

> +/* Add new subset to list.  */

>

> -      *flags &= ~MASK_RVE;

> +void riscv_subset_list::add (const char *subset,

> +                            int major_version,

> +                            int minor_version)

> +{

> +  riscv_subset_t *s = new riscv_subset_t ();

>

> -      *flags |= MASK_MUL;

> -      *flags |= MASK_ATOMIC;

> -      *flags |= MASK_HARD_FLOAT;

> -      *flags |= MASK_DOUBLE_FLOAT;

> -    }

> -  else if (*p == 'i')

> -    {

> -      p++;

> +  if (m_head == NULL)

> +    m_head = s;

> +

> +  s->name = subset;

> +  s->major_version = major_version;

> +  s->minor_version = minor_version;

> +  s->next = NULL;

> +

> +  if (m_tail != NULL)

> +    m_tail->next = s;

> +

> +  m_tail = s;

> +}

>

> -      *flags &= ~MASK_RVE;

> +/* Find subset in list without version checking, return NULL if not found.  */

>

> -      *flags &= ~MASK_MUL;

> -      if (*p == 'm')

> -       *flags |= MASK_MUL, p++;

> +riscv_subset_t *riscv_subset_list::lookup (const char *subset,

> +                                          int major_version,

> +                                          int minor_version) const

> +{

> +  riscv_subset_t *s;

> +

> +  for (s = m_head; s != NULL; s = s->next)

> +    if (strcasecmp (s->name.c_str(), subset) == 0)

> +      {

> +       if ((major_version != RISCV_DONT_CARE_VERSION)

> +           && (s->major_version != major_version))

> +         return NULL;

> +

> +       if ((minor_version != RISCV_DONT_CARE_VERSION)

> +           && (s->minor_version != minor_version))

> +         return NULL;

>

> -      *flags &= ~MASK_ATOMIC;

> -      if (*p == 'a')

> -       *flags |= MASK_ATOMIC, p++;

> +       return s;

> +      }

> +

> +  return s;

> +}

> +

> +/* Return string which contain all supported standard extensions in

> +   canonical order.  */

> +

> +static const char *

> +riscv_supported_std_ext (void)

> +{

> +  return "mafdqlcbjtpvn";

> +}

> +

> +/* Parsing subset version.

> +

> +   Return Value:

> +     Points to the end of version

> +

> +   Arguments:

> +     `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.  */

> +

> +const char *

> +riscv_subset_list::parsing_subset_version (const char *p,

> +                                          unsigned *major_version,

> +                                          unsigned *minor_version,

> +                                          unsigned default_major_version,

> +                                          unsigned default_minor_version,

> +                                          bool std_ext_p)

> +{

> +  bool major_p = true;

> +  unsigned version = 0;

> +  unsigned major = 0;

> +  unsigned minor = 0;

> +  char np;

>

> -      *flags &= ~(MASK_HARD_FLOAT | MASK_DOUBLE_FLOAT);

> -      if (*p == 'f')

> +  for (;*p; ++p)

> +    {

> +      if (*p == 'p')

>         {

> -         *flags |= MASK_HARD_FLOAT, p++;

> +         np = *(p + 1);

>

> -         if (*p == 'd')

> +         if (!ISDIGIT (np))

>             {

> -             *flags |= MASK_DOUBLE_FLOAT;

> -             p++;

> +             /* Might be beginning of `p` extension.  */

> +             if (std_ext_p)

> +               {

> +                 *major_version = version;

> +                 *minor_version = 0;

> +                 return p;

> +               }

> +             else

> +               {

> +                 error_at (m_loc, "-march=%s: Expect number after `%dp'.",

> +                           m_arch, version);

> +                 return NULL;

> +               }

>             }

> +

> +         major = version;

> +         major_p = false;

> +         version = 0;

>         }

> +      else if (ISDIGIT (*p))

> +       version = (version * 10) + (*p - '0');

> +      else

> +       break;

>      }

> -  else if (*p == 'e')

> +

> +  if (major_p)

> +    major = version;

> +  else

> +    minor = version;

> +

> +  if (major == 0 && minor == 0)

>      {

> -      p++;

> +      /* 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;

> +}

> +

> +/* Parsing function for standard extensions.

> +

> +   Return Value:

> +     Points to the end of extensions.

> +

> +   Arguments:

> +     `p`: Curent parsing position.  */

>

> -      *flags |= MASK_RVE;

> +const char *

> +riscv_subset_list::parse_std_ext (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 = parsing_subset_version (

> +             p, &major_version, &minor_version,

> +             /* default_major_version= */ 2,

> +             /* default_minor_version= */ 0,

> +             /* std_ext_p= */true);

> +       add ("i", major_version, minor_version);

> +       break;

>

> -      if (*flags & MASK_64BIT)

> +      case 'e':

> +       p++;

> +       p = parsing_subset_version (

> +             p, &major_version, &minor_version,

> +             /* default_major_version= */ 1,

> +             /* default_minor_version= */ 9,

> +             /* std_ext_p= */true);

> +

> +       add ("e", major_version, minor_version);

> +

> +       if (m_xlen > 32)

> +         {

> +           error_at (m_loc, "-march=%s: rv%de is not a valid base ISA",

> +                     m_arch, m_xlen);

> +           return NULL;

> +         }

> +

> +       break;

> +

> +      case 'g':

> +       p++;

> +       p = parsing_subset_version (

> +             p, &major_version, &minor_version,

> +             /* default_major_version= */ 2,

> +             /* default_minor_version= */ 0,

> +             /* std_ext_p= */true);

> +       add ("i", major_version, minor_version);

> +

> +       for ( ; *std_exts != 'q'; std_exts++)

> +         {

> +           const char subset[] = {*std_exts, '\0'};

> +           add (subset, major_version, minor_version);

> +         }

> +       break;

> +

> +      default:

> +       error_at (m_loc, "-march=%s: first ISA subset must be `e', `i' or `g'",

> +                 m_arch);

> +       return NULL;

> +    }

> +

> +  while (*p)

> +    {

> +      char subset[2] = {0, 0};

> +

> +      if (*p == 'x' || *p == 's')

> +       break;

> +

> +      if (*p == '_')

>         {

> -         error ("RV64E is not a valid base ISA");

> -         return;

> +         p++;

> +         continue;

>         }

>

> -      *flags &= ~MASK_MUL;

> -      if (*p == 'm')

> -       *flags |= MASK_MUL, p++;

> +      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)

> +           error_at (m_loc, "-march=%s: unsupported ISA subset `%c'",

> +                     m_arch, *p);

> +         else

> +           error_at (m_loc,

> +                     "-march=%s: ISA string is not in canonical order. `%c'",

> +                     m_arch, *p);

> +         return NULL;

> +       }

>

> -      *flags &= ~MASK_ATOMIC;

> -      if (*p == 'a')

> -       *flags |= MASK_ATOMIC, p++;

> +      std_exts++;

>

> -      *flags &= ~(MASK_HARD_FLOAT | MASK_DOUBLE_FLOAT);

> +      p++;

> +      p = parsing_subset_version (

> +           p, &major_version, &minor_version,

> +           /* default_major_version= */ 2,

> +           /* default_minor_version= */ 0,

> +           /* std_ext_p= */true);

> +

> +      subset[0] = std_ext;

> +

> +      add (subset, major_version, minor_version);

>      }

> -  else

> +  return p;

> +}

> +

> +/* Parsing function for non-standard and supervisor extensions.

> +

> +   Return Value:

> +     Points to the end of extensions.

> +

> +   Arguments:

> +     `p`: Curent parsing position.

> +     `ext_type`: What kind of extensions, 'x', 's' or 'sx'.

> +     `ext_type_str`: Full name for kind of extension.  */

> +

> +const char *

> +riscv_subset_list::parse_sv_or_non_std_ext (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)

>      {

> -      error_at (loc, "-march=%s: invalid ISA string", isa);

> -      return;

> +      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 =

> +       parsing_subset_version (

> +         q, &major_version, &minor_version,

> +         /* default_major_version= */ 2,

> +         /* default_minor_version= */ 0,

> +         /* std_ext_p= */FALSE);

> +

> +      *q = '\0';

> +

> +      add (subset, major_version, minor_version);

> +      free (subset);

> +      p += end_of_version - subset;

> +

> +      if (*p != '\0' && *p != '_')

> +       {

> +         error_at (m_loc, "-march=%s: %s must seperate with _",

> +                   m_arch, ext_type_str);

> +         return NULL;

> +       }

>      }

>

> -  *flags &= ~MASK_RVC;

> -  if (*p == 'c')

> -    *flags |= MASK_RVC, p++;

> +  return p;

> +}

> +

> +/* Parsing arch string to subset list, return NULL if parsing failed.  */

>

> -  if (*p)

> +riscv_subset_list *

> +riscv_subset_list::parse (const char *arch, location_t loc)

> +{

> +  riscv_subset_list *subset_list = new riscv_subset_list (arch, loc);

> +  const char *p = arch;

> +  if (strncmp (p, "rv32", 4) == 0)

>      {

> -      error_at (loc, "-march=%s: unsupported ISA substring %qs", isa, p);

> -      return;

> +      subset_list->m_xlen = 32;

> +      p += 4;

>      }

> +  else if (strncmp (p, "rv64", 4) == 0)

> +    {

> +      subset_list->m_xlen = 64;

> +      p += 4;

> +    }

> +  else

> +    {

> +      error_at (loc, "-march=%s: ISA string must begin with rv32 or rv64",

> +               arch);

> +      goto fail;

> +    }

> +

> +  /* Parsing standard extension.  */

> +  p = subset_list->parse_std_ext (p);

> +

> +  if (p == NULL)

> +    goto fail;

> +

> +  /* Parsing non-standard extension.  */

> +  p = subset_list->parse_sv_or_non_std_ext (

> +       p, "x", "non-standard extension");

> +

> +  if (p == NULL)

> +    goto fail;

> +

> +  /* Parsing supervisor extension.  */

> +  p = subset_list->parse_sv_or_non_std_ext (

> +       p, "s", "supervisor extension");

> +

> +  if (p == NULL)

> +    goto fail;

> +

> +  /* Parsing non-standard supervisor extension.  */

> +  p = subset_list->parse_sv_or_non_std_ext (

> +       p, "sx", "non-standard supervisor extension");

> +

> +  if (p == NULL)

> +    goto fail;

> +

> +  return subset_list;

> +

> +fail:

> +  delete subset_list;

> +  return NULL;

> +}

> +

> +/* Parse a RISC-V ISA string into an option mask.  Must clear or set all arch

> +   dependent mask bits, in case more than one -march string is passed.  */

> +

> +static void

> +riscv_parse_arch_string (const char *isa, int *flags, location_t loc)

> +{

> +  riscv_subset_list *subset_list;

> +  subset_list = riscv_subset_list::parse (isa, loc);

> +  if (!subset_list)

> +    return;

> +

> +  if (subset_list->xlen () == 32)

> +    *flags &= ~MASK_64BIT;

> +  else if (subset_list->xlen () == 64)

> +    *flags |= MASK_64BIT;

> +

> +  *flags &= ~MASK_RVE;

> +  if (subset_list->lookup ("e"))

> +     *flags |= MASK_RVE;

> +

> +  *flags &= ~MASK_MUL;

> +  if (subset_list->lookup ("m"))

> +    *flags |= MASK_MUL;

> +

> +  *flags &= ~MASK_ATOMIC;

> +  if (subset_list->lookup ("a"))

> +    *flags |= MASK_ATOMIC;

> +

> +  *flags &= ~(MASK_HARD_FLOAT | MASK_DOUBLE_FLOAT);

> +  if (subset_list->lookup ("f"))

> +    *flags |= MASK_HARD_FLOAT;

> +

> +  if (subset_list->lookup ("d"))

> +    *flags |= MASK_DOUBLE_FLOAT;

> +

> +  if (current_subset_list)

> +    delete current_subset_list;

> +

> +  current_subset_list = subset_list;

>  }

>

>  /* Implement TARGET_HANDLE_OPTION.  */

> diff --git a/gcc/testsuite/gcc.target/riscv/arch-1.c b/gcc/testsuite/gcc.target/riscv/arch-1.c

> new file mode 100644

> index 0000000..e7c8738

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/riscv/arch-1.c

> @@ -0,0 +1,7 @@

> +/* Verify the return instruction is mret.  */

> +/* { dg-do compile } */

> +/* { dg-options "-O -march=rv32I -mabi=ilp32" } */

> +int foo()

> +{

> +}

> +/* { dg-error ".-march=rv32I: first ISA subset must be `e', `i' or `g'" "" { target *-*-* } 0 } */

> diff --git a/gcc/testsuite/gcc.target/riscv/arch-2.c b/gcc/testsuite/gcc.target/riscv/arch-2.c

> new file mode 100644

> index 0000000..ed90e2f

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/riscv/arch-2.c

> @@ -0,0 +1,6 @@

> +/* Verify the return instruction is mret.  */

> +/* { dg-do compile } */

> +/* { dg-options "-O -march=rv32ixabc_xfoo -mabi=ilp32" } */

> +int foo()

> +{

> +}

> diff --git a/gcc/testsuite/gcc.target/riscv/arch-3.c b/gcc/testsuite/gcc.target/riscv/arch-3.c

> new file mode 100644

> index 0000000..55908dd

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/riscv/arch-3.c

> @@ -0,0 +1,6 @@

> +/* Verify the return instruction is mret.  */

> +/* { dg-do compile } */

> +/* { dg-options "-O -march=rv32ixbar_sabc_sxfoo -mabi=ilp32" } */

> +int foo()

> +{

> +}

> diff --git a/gcc/testsuite/gcc.target/riscv/arch-4.c b/gcc/testsuite/gcc.target/riscv/arch-4.c

> new file mode 100644

> index 0000000..3c9e1c1

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/riscv/arch-4.c

> @@ -0,0 +1,6 @@

> +/* Verify the return instruction is mret.  */

> +/* { dg-do compile } */

> +/* { dg-options "-O -march=rv32i2p3_m4p2 -mabi=ilp32" } */

> +int foo()

> +{

> +}

> --

> 1.8.3.1

>

Patch

diff --git a/gcc/common/config/riscv/riscv-common.c b/gcc/common/config/riscv/riscv-common.c
index cb5bb7f..e412acb 100644
--- a/gcc/common/config/riscv/riscv-common.c
+++ b/gcc/common/config/riscv/riscv-common.c
@@ -17,6 +17,7 @@  You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
+#define INCLUDE_STRING
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
@@ -26,99 +27,510 @@  along with GCC; see the file COPYING3.  If not see
 #include "opts.h"
 #include "flags.h"
 #include "diagnostic-core.h"
+#include "config/riscv/riscv-protos.h"
 
-/* Parse a RISC-V ISA string into an option mask.  Must clear or set all arch
-   dependent mask bits, in case more than one -march string is passed.  */
+#define RISCV_DONT_CARE_VERSION -1
 
-static void
-riscv_parse_arch_string (const char *isa, int *flags, location_t loc)
+/* Subset info.  */
+struct riscv_subset_t {
+  riscv_subset_t ();
+
+  std::string name;
+  int major_version;
+  int minor_version;
+  struct riscv_subset_t *next;
+};
+
+/* Subset list.  */
+class riscv_subset_list {
+private:
+  /* Original arch string.  */
+  const char *m_arch;
+
+  /* Localtion of arch string, used for report error.  */
+  location_t m_loc;
+
+  /* Head of subset info list.  */
+  riscv_subset_t *m_head;
+
+  /* Tail of subset info list.  */
+  riscv_subset_t *m_tail;
+
+  /* X-len of m_arch. */
+  unsigned m_xlen;
+
+  riscv_subset_list (const char *, location_t);
+
+  const char *parsing_subset_version (const char *, unsigned *, unsigned *,
+				      unsigned, unsigned, bool);
+
+  const char *parse_std_ext (const char *);
+
+  const char *parse_sv_or_non_std_ext (const char *, const char *,
+				       const char *);
+
+public:
+  ~riscv_subset_list ();
+
+  void add (const char *, int, int);
+
+  riscv_subset_t *lookup (const char *,
+			  int major_version = RISCV_DONT_CARE_VERSION,
+			  int minor_version = RISCV_DONT_CARE_VERSION) const;
+
+  unsigned xlen() const {return m_xlen;};
+
+  static riscv_subset_list *parse (const char *, location_t);
+
+};
+
+static const char *riscv_supported_std_ext (void);
+
+static riscv_subset_list *current_subset_list = NULL;
+
+riscv_subset_t::riscv_subset_t()
+: name (), major_version (0), minor_version (0), next (NULL)
 {
-  const char *p = isa;
+}
 
-  if (strncmp (p, "rv32", 4) == 0)
-    *flags &= ~MASK_64BIT, p += 4;
-  else if (strncmp (p, "rv64", 4) == 0)
-    *flags |= MASK_64BIT, p += 4;
-  else
+riscv_subset_list::riscv_subset_list (const char *arch, location_t loc)
+: m_arch (arch), m_loc(loc), m_head (NULL), m_tail (NULL), m_xlen(0)
+{
+}
+
+riscv_subset_list::~riscv_subset_list()
+{
+  if (!m_head)
+    return;
+
+  riscv_subset_t *item = this->m_head->next;
+  while (item != NULL)
     {
-      error_at (loc, "-march=%s: ISA string must begin with rv32 or rv64", isa);
-      return;
+      riscv_subset_t *next = item->next;
+      delete item;
+      item = next;
     }
+}
 
-  if (*p == 'g')
-    {
-      p++;
+/* Add new subset to list.  */
 
-      *flags &= ~MASK_RVE;
+void riscv_subset_list::add (const char *subset,
+			     int major_version,
+			     int minor_version)
+{
+  riscv_subset_t *s = new riscv_subset_t ();
 
-      *flags |= MASK_MUL;
-      *flags |= MASK_ATOMIC;
-      *flags |= MASK_HARD_FLOAT;
-      *flags |= MASK_DOUBLE_FLOAT;
-    }
-  else if (*p == 'i')
-    {
-      p++;
+  if (m_head == NULL)
+    m_head = s;
+
+  s->name = subset;
+  s->major_version = major_version;
+  s->minor_version = minor_version;
+  s->next = NULL;
+
+  if (m_tail != NULL)
+    m_tail->next = s;
+
+  m_tail = s;
+}
 
-      *flags &= ~MASK_RVE;
+/* Find subset in list without version checking, return NULL if not found.  */
 
-      *flags &= ~MASK_MUL;
-      if (*p == 'm')
-	*flags |= MASK_MUL, p++;
+riscv_subset_t *riscv_subset_list::lookup (const char *subset,
+					   int major_version,
+					   int minor_version) const
+{
+  riscv_subset_t *s;
+
+  for (s = m_head; s != NULL; s = s->next)
+    if (strcasecmp (s->name.c_str(), subset) == 0)
+      {
+	if ((major_version != RISCV_DONT_CARE_VERSION)
+	    && (s->major_version != major_version))
+	  return NULL;
+
+	if ((minor_version != RISCV_DONT_CARE_VERSION)
+	    && (s->minor_version != minor_version))
+	  return NULL;
 
-      *flags &= ~MASK_ATOMIC;
-      if (*p == 'a')
-	*flags |= MASK_ATOMIC, p++;
+	return s;
+      }
+
+  return s;
+}
+
+/* Return string which contain all supported standard extensions in
+   canonical order.  */
+
+static const char *
+riscv_supported_std_ext (void)
+{
+  return "mafdqlcbjtpvn";
+}
+
+/* Parsing subset version.
+
+   Return Value:
+     Points to the end of version
+
+   Arguments:
+     `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.  */
+
+const char *
+riscv_subset_list::parsing_subset_version (const char *p,
+					   unsigned *major_version,
+					   unsigned *minor_version,
+					   unsigned default_major_version,
+					   unsigned default_minor_version,
+					   bool std_ext_p)
+{
+  bool major_p = true;
+  unsigned version = 0;
+  unsigned major = 0;
+  unsigned minor = 0;
+  char np;
 
-      *flags &= ~(MASK_HARD_FLOAT | MASK_DOUBLE_FLOAT);
-      if (*p == 'f')
+  for (;*p; ++p)
+    {
+      if (*p == 'p')
 	{
-	  *flags |= MASK_HARD_FLOAT, p++;
+	  np = *(p + 1);
 
-	  if (*p == 'd')
+	  if (!ISDIGIT (np))
 	    {
-	      *flags |= MASK_DOUBLE_FLOAT;
-	      p++;
+	      /* Might be beginning of `p` extension.  */
+	      if (std_ext_p)
+		{
+		  *major_version = version;
+		  *minor_version = 0;
+		  return p;
+		}
+	      else
+		{
+		  error_at (m_loc, "-march=%s: Expect number after `%dp'.",
+			    m_arch, version);
+		  return NULL;
+		}
 	    }
+
+	  major = version;
+	  major_p = false;
+	  version = 0;
 	}
+      else if (ISDIGIT (*p))
+	version = (version * 10) + (*p - '0');
+      else
+	break;
     }
-  else if (*p == 'e')
+
+  if (major_p)
+    major = version;
+  else
+    minor = version;
+
+  if (major == 0 && minor == 0)
     {
-      p++;
+      /* 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;
+}
+
+/* Parsing function for standard extensions.
+
+   Return Value:
+     Points to the end of extensions.
+
+   Arguments:
+     `p`: Curent parsing position.  */
 
-      *flags |= MASK_RVE;
+const char *
+riscv_subset_list::parse_std_ext (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 = parsing_subset_version (
+	      p, &major_version, &minor_version,
+	      /* default_major_version= */ 2,
+	      /* default_minor_version= */ 0,
+	      /* std_ext_p= */true);
+	add ("i", major_version, minor_version);
+	break;
 
-      if (*flags & MASK_64BIT)
+      case 'e':
+	p++;
+	p = parsing_subset_version (
+	      p, &major_version, &minor_version,
+	      /* default_major_version= */ 1,
+	      /* default_minor_version= */ 9,
+	      /* std_ext_p= */true);
+
+	add ("e", major_version, minor_version);
+
+	if (m_xlen > 32)
+	  {
+	    error_at (m_loc, "-march=%s: rv%de is not a valid base ISA",
+		      m_arch, m_xlen);
+	    return NULL;
+	  }
+
+	break;
+
+      case 'g':
+	p++;
+	p = parsing_subset_version (
+	      p, &major_version, &minor_version,
+	      /* default_major_version= */ 2,
+	      /* default_minor_version= */ 0,
+	      /* std_ext_p= */true);
+	add ("i", major_version, minor_version);
+
+	for ( ; *std_exts != 'q'; std_exts++)
+	  {
+	    const char subset[] = {*std_exts, '\0'};
+	    add (subset, major_version, minor_version);
+	  }
+	break;
+
+      default:
+	error_at (m_loc, "-march=%s: first ISA subset must be `e', `i' or `g'",
+		  m_arch);
+	return NULL;
+    }
+
+  while (*p)
+    {
+      char subset[2] = {0, 0};
+
+      if (*p == 'x' || *p == 's')
+	break;
+
+      if (*p == '_')
 	{
-	  error ("RV64E is not a valid base ISA");
-	  return;
+	  p++;
+	  continue;
 	}
 
-      *flags &= ~MASK_MUL;
-      if (*p == 'm')
-	*flags |= MASK_MUL, p++;
+      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)
+	    error_at (m_loc, "-march=%s: unsupported ISA subset `%c'",
+		      m_arch, *p);
+	  else
+	    error_at (m_loc,
+		      "-march=%s: ISA string is not in canonical order. `%c'",
+		      m_arch, *p);
+	  return NULL;
+	}
 
-      *flags &= ~MASK_ATOMIC;
-      if (*p == 'a')
-	*flags |= MASK_ATOMIC, p++;
+      std_exts++;
 
-      *flags &= ~(MASK_HARD_FLOAT | MASK_DOUBLE_FLOAT);
+      p++;
+      p = parsing_subset_version (
+	    p, &major_version, &minor_version,
+	    /* default_major_version= */ 2,
+	    /* default_minor_version= */ 0,
+	    /* std_ext_p= */true);
+
+      subset[0] = std_ext;
+
+      add (subset, major_version, minor_version);
     }
-  else
+  return p;
+}
+
+/* Parsing function for non-standard and supervisor extensions.
+
+   Return Value:
+     Points to the end of extensions.
+
+   Arguments:
+     `p`: Curent parsing position.
+     `ext_type`: What kind of extensions, 'x', 's' or 'sx'.
+     `ext_type_str`: Full name for kind of extension.  */
+
+const char *
+riscv_subset_list::parse_sv_or_non_std_ext (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)
     {
-      error_at (loc, "-march=%s: invalid ISA string", isa);
-      return;
+      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 =
+	parsing_subset_version (
+	  q, &major_version, &minor_version,
+	  /* default_major_version= */ 2,
+	  /* default_minor_version= */ 0,
+	  /* std_ext_p= */FALSE);
+
+      *q = '\0';
+
+      add (subset, major_version, minor_version);
+      free (subset);
+      p += end_of_version - subset;
+
+      if (*p != '\0' && *p != '_')
+	{
+	  error_at (m_loc, "-march=%s: %s must seperate with _",
+		    m_arch, ext_type_str);
+	  return NULL;
+	}
     }
 
-  *flags &= ~MASK_RVC;
-  if (*p == 'c')
-    *flags |= MASK_RVC, p++;
+  return p;
+}
+
+/* Parsing arch string to subset list, return NULL if parsing failed.  */
 
-  if (*p)
+riscv_subset_list *
+riscv_subset_list::parse (const char *arch, location_t loc)
+{
+  riscv_subset_list *subset_list = new riscv_subset_list (arch, loc);
+  const char *p = arch;
+  if (strncmp (p, "rv32", 4) == 0)
     {
-      error_at (loc, "-march=%s: unsupported ISA substring %qs", isa, p);
-      return;
+      subset_list->m_xlen = 32;
+      p += 4;
     }
+  else if (strncmp (p, "rv64", 4) == 0)
+    {
+      subset_list->m_xlen = 64;
+      p += 4;
+    }
+  else
+    {
+      error_at (loc, "-march=%s: ISA string must begin with rv32 or rv64",
+		arch);
+      goto fail;
+    }
+
+  /* Parsing standard extension.  */
+  p = subset_list->parse_std_ext (p);
+
+  if (p == NULL)
+    goto fail;
+
+  /* Parsing non-standard extension.  */
+  p = subset_list->parse_sv_or_non_std_ext (
+	p, "x", "non-standard extension");
+
+  if (p == NULL)
+    goto fail;
+
+  /* Parsing supervisor extension.  */
+  p = subset_list->parse_sv_or_non_std_ext (
+	p, "s", "supervisor extension");
+
+  if (p == NULL)
+    goto fail;
+
+  /* Parsing non-standard supervisor extension.  */
+  p = subset_list->parse_sv_or_non_std_ext (
+	p, "sx", "non-standard supervisor extension");
+
+  if (p == NULL)
+    goto fail;
+
+  return subset_list;
+
+fail:
+  delete subset_list;
+  return NULL;
+}
+
+/* Parse a RISC-V ISA string into an option mask.  Must clear or set all arch
+   dependent mask bits, in case more than one -march string is passed.  */
+
+static void
+riscv_parse_arch_string (const char *isa, int *flags, location_t loc)
+{
+  riscv_subset_list *subset_list;
+  subset_list = riscv_subset_list::parse (isa, loc);
+  if (!subset_list)
+    return;
+
+  if (subset_list->xlen () == 32)
+    *flags &= ~MASK_64BIT;
+  else if (subset_list->xlen () == 64)
+    *flags |= MASK_64BIT;
+
+  *flags &= ~MASK_RVE;
+  if (subset_list->lookup ("e"))
+     *flags |= MASK_RVE;
+
+  *flags &= ~MASK_MUL;
+  if (subset_list->lookup ("m"))
+    *flags |= MASK_MUL;
+
+  *flags &= ~MASK_ATOMIC;
+  if (subset_list->lookup ("a"))
+    *flags |= MASK_ATOMIC;
+
+  *flags &= ~(MASK_HARD_FLOAT | MASK_DOUBLE_FLOAT);
+  if (subset_list->lookup ("f"))
+    *flags |= MASK_HARD_FLOAT;
+
+  if (subset_list->lookup ("d"))
+    *flags |= MASK_DOUBLE_FLOAT;
+
+  if (current_subset_list)
+    delete current_subset_list;
+
+  current_subset_list = subset_list;
 }
 
 /* Implement TARGET_HANDLE_OPTION.  */
diff --git a/gcc/testsuite/gcc.target/riscv/arch-1.c b/gcc/testsuite/gcc.target/riscv/arch-1.c
new file mode 100644
index 0000000..e7c8738
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/arch-1.c
@@ -0,0 +1,7 @@ 
+/* Verify the return instruction is mret.  */
+/* { dg-do compile } */
+/* { dg-options "-O -march=rv32I -mabi=ilp32" } */
+int foo()
+{
+}
+/* { dg-error ".-march=rv32I: first ISA subset must be `e', `i' or `g'" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/gcc.target/riscv/arch-2.c b/gcc/testsuite/gcc.target/riscv/arch-2.c
new file mode 100644
index 0000000..ed90e2f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/arch-2.c
@@ -0,0 +1,6 @@ 
+/* Verify the return instruction is mret.  */
+/* { dg-do compile } */
+/* { dg-options "-O -march=rv32ixabc_xfoo -mabi=ilp32" } */
+int foo()
+{
+}
diff --git a/gcc/testsuite/gcc.target/riscv/arch-3.c b/gcc/testsuite/gcc.target/riscv/arch-3.c
new file mode 100644
index 0000000..55908dd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/arch-3.c
@@ -0,0 +1,6 @@ 
+/* Verify the return instruction is mret.  */
+/* { dg-do compile } */
+/* { dg-options "-O -march=rv32ixbar_sabc_sxfoo -mabi=ilp32" } */
+int foo()
+{
+}
diff --git a/gcc/testsuite/gcc.target/riscv/arch-4.c b/gcc/testsuite/gcc.target/riscv/arch-4.c
new file mode 100644
index 0000000..3c9e1c1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/arch-4.c
@@ -0,0 +1,6 @@ 
+/* Verify the return instruction is mret.  */
+/* { dg-do compile } */
+/* { dg-options "-O -march=rv32i2p3_m4p2 -mabi=ilp32" } */
+int foo()
+{
+}