along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
+#include <sstream>
+
+#define INCLUDE_STRING
#include "config.h"
#include "system.h"
#include "coretypes.h"
#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
{
- const char *p = isa;
+ riscv_subset_t ();
- if (strncmp (p, "rv32", 4) == 0)
- *flags &= ~MASK_64BIT, p += 4;
- else if (strncmp (p, "rv64", 4) == 0)
- *flags |= MASK_64BIT, p += 4;
- else
+ 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;
+
+ /* Location 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;
+
+ std::string to_string () 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)
+{
+}
+
+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;
+ 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')
+ 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;
+}
+
+/* Convert subset info to string with explicit version info. */
+
+std::string
+riscv_subset_list::to_string () const
+{
+ std::ostringstream oss;
+ oss << "rv" << m_xlen;
+
+ bool first = true;
+ riscv_subset_t *subset = m_head;
+
+ while (subset != NULL)
{
- p++;
+ if (!first)
+ oss << '_';
+ first = false;
+
+ oss << subset->name
+ << subset->major_version
+ << 'p'
+ << subset->minor_version;
+ subset = subset->next;
+ }
+
+ return oss.str ();
+}
+
+/* Find subset in list with version checking, return NULL if not found.
+ major/minor version checking can be ignored if major_version/minor_version
+ is RISCV_DONT_CARE_VERSION. */
+
+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;
+
+ return s;
+ }
+
+ return s;
+}
+
+/* Return string which contains all supported standard extensions in
+ canonical order. */
- *flags &= ~MASK_RVE;
+static const char *
+riscv_supported_std_ext (void)
+{
+ return "mafdqlcbjtpvn";
+}
+
+/* Parsing subset version.
- *flags &= ~MASK_MUL;
- if (*p == 'm')
- *flags |= MASK_MUL, p++;
+ Return Value:
+ Points to the end of version
- *flags &= ~MASK_ATOMIC;
- if (*p == 'a')
- *flags |= MASK_ATOMIC, p++;
+ Arguments:
+ `p`: Current 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. */
- *flags &= ~(MASK_HARD_FLOAT | MASK_DOUBLE_FLOAT);
- if (*p == 'f')
+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;
+
+ 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)
+ {
+ /* We didn't find 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`: Current parsing position. */
+
+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;
+
+ case 'e':
p++;
+ p = parsing_subset_version (p, &major_version, &minor_version,
+ /* default_major_version= */ 1,
+ /* default_minor_version= */ 9,
+ /* std_ext_p= */ true);
- *flags |= MASK_RVE;
+ add ("e", major_version, minor_version);
- if (*flags & MASK_64BIT)
+ if (m_xlen > 32)
{
- error ("RV64E is not a valid base ISA");
- return;
+ error_at (m_loc, "-march=%s: rv%de is not a valid base ISA", m_arch,
+ m_xlen);
+ return NULL;
}
+ break;
- *flags &= ~MASK_MUL;
- if (*p == 'm')
- *flags |= MASK_MUL, p++;
+ 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);
- *flags &= ~MASK_ATOMIC;
- if (*p == 'a')
- *flags |= MASK_ATOMIC, p++;
+ for (; *std_exts != 'q'; std_exts++)
+ {
+ const char subset[] = {*std_exts, '\0'};
+ add (subset, major_version, minor_version);
+ }
+ break;
- *flags &= ~(MASK_HARD_FLOAT | MASK_DOUBLE_FLOAT);
+ default:
+ error_at (m_loc, "-march=%s: first ISA subset must be `e', `i' or `g'",
+ m_arch);
+ return NULL;
}
- else
+
+ while (*p)
{
- error_at (loc, "-march=%s: invalid ISA string", isa);
- return;
+ 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)
+ 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;
+ }
+
+ std_exts++;
+
+ 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);
}
+ return p;
+}
- *flags &= ~MASK_RVC;
- if (*p == 'c')
- *flags |= MASK_RVC, p++;
+/* Parsing function for non-standard and supervisor extensions.
- if (*p)
+ Return Value:
+ Points to the end of extensions.
+
+ Arguments:
+ `p`: Current 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: unsupported ISA substring %qs", isa, p);
- 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 separate with _",
+ m_arch, ext_type_str);
+ return NULL;
+ }
}
+
+ return p;
+}
+
+/* Parsing arch string to subset list, return NULL if parsing failed. */
+
+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)
+ {
+ 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;
+}
+
+/* Return the current arch string. */
+
+std::string
+riscv_arch_str ()
+{
+ gcc_assert (current_subset_list);
+ return current_subset_list->to_string ();
+}
+
+/* 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. */