/* Common hooks for RISC-V. Copyright (C) 2016-2020 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include #define INCLUDE_STRING #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "common/common-target.h" #include "common/common-target-def.h" #include "opts.h" #include "flags.h" #include "diagnostic-core.h" #include "config/riscv/riscv-protos.h" #define RISCV_DONT_CARE_VERSION -1 /* 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; /* 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) { riscv_subset_t *next = item->next; delete item; item = next; } } /* Add new subset to list. */ void riscv_subset_list::add (const char *subset, int major_version, int minor_version) { riscv_subset_t *s = new riscv_subset_t (); 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) { 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. */ static const char * riscv_supported_std_ext (void) { return "mafdqlcbjtpvn"; } /* Parsing subset version. Return Value: Points to the end of version 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. */ 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') { 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 { 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; } 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); 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 %, " "% or %", m_arch); 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) 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; } /* Parsing function for non-standard and supervisor extensions. 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) { 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; if (*p != '\0') { error_at (loc, "%<-march=%s%>: unexpected ISA string at end: %qs", arch, p); 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; *flags &= ~MASK_RVC; if (subset_list->lookup ("c")) *flags |= MASK_RVC; if (current_subset_list) delete current_subset_list; current_subset_list = subset_list; } /* Implement TARGET_HANDLE_OPTION. */ static bool riscv_handle_option (struct gcc_options *opts, struct gcc_options *opts_set ATTRIBUTE_UNUSED, const struct cl_decoded_option *decoded, location_t loc) { switch (decoded->opt_index) { case OPT_march_: riscv_parse_arch_string (decoded->arg, &opts->x_target_flags, loc); return true; default: return true; } } /* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */ static const struct default_options riscv_option_optimization_table[] = { { OPT_LEVELS_1_PLUS, OPT_fsection_anchors, NULL, 1 }, { OPT_LEVELS_2_PLUS, OPT_free, NULL, 1 }, { OPT_LEVELS_NONE, 0, NULL, 0 } }; #undef TARGET_OPTION_OPTIMIZATION_TABLE #define TARGET_OPTION_OPTIMIZATION_TABLE riscv_option_optimization_table #undef TARGET_HANDLE_OPTION #define TARGET_HANDLE_OPTION riscv_handle_option struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER;